diff options
Diffstat (limited to 'sql')
127 files changed, 15734 insertions, 10767 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index d85e588abeb..83bf909192e 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -152,6 +152,7 @@ IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR ADD_DEFINITIONS(-DHAVE_POOL_OF_THREADS) IF(WIN32) SET(SQL_SOURCE ${SQL_SOURCE} threadpool_win.cc) + SET(SQL_SOURCE ${SQL_SOURCE} handle_connections_win.cc) ENDIF() SET(SQL_SOURCE ${SQL_SOURCE} threadpool_generic.cc) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 86a710f87c6..a6803982171 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -654,7 +654,7 @@ my_time_t add_interval(MYSQL_TIME *ltime, const Time_zone *time_zone, interval_type scale, INTERVAL interval) { - if (date_add_interval(ltime, scale, interval)) + if (date_add_interval(current_thd, ltime, scale, interval)) return 0; uint not_used; @@ -758,8 +758,8 @@ bool get_next_time(const Time_zone *time_zone, my_time_t *next, if (seconds) { - longlong seconds_diff; - long microsec_diff; + ulonglong seconds_diff; + ulong microsec_diff; bool negative= calc_time_diff(&local_now, &local_start, 1, &seconds_diff, µsec_diff); if (!negative) diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc index b2ff80626db..bfda8438885 100644 --- a/sql/event_parse_data.cc +++ b/sql/event_parse_data.cc @@ -216,7 +216,7 @@ Event_parse_data::init_execute_at(THD *thd) (starts_null && ends_null))); DBUG_ASSERT(starts_null && ends_null); - if (item_execute_at->get_date(<ime, TIME_NO_ZERO_DATE)) + if (item_execute_at->get_date(thd, <ime, TIME_NO_ZERO_DATE)) goto wrong_value; ltime_utc= TIME_to_timestamp(thd,<ime,¬_used); @@ -275,7 +275,7 @@ Event_parse_data::init_interval(THD *thd) if (item_expression->fix_fields(thd, &item_expression)) goto wrong_value; - if (get_interval_value(item_expression, interval, &interval_tmp)) + if (get_interval_value(thd, item_expression, interval, &interval_tmp)) goto wrong_value; expression= 0; @@ -378,7 +378,7 @@ Event_parse_data::init_starts(THD *thd) if (item_starts->fix_fields(thd, &item_starts)) goto wrong_value; - if (item_starts->get_date(<ime, TIME_NO_ZERO_DATE)) + if (item_starts->get_date(thd, <ime, TIME_NO_ZERO_DATE)) goto wrong_value; ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); @@ -433,7 +433,7 @@ Event_parse_data::init_ends(THD *thd) goto error_bad_params; DBUG_PRINT("info", ("convert to TIME")); - if (item_ends->get_date(<ime, TIME_NO_ZERO_DATE)) + if (item_ends->get_date(thd, <ime, TIME_NO_ZERO_DATE)) goto error_bad_params; ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); @@ -472,7 +472,7 @@ Event_parse_data::report_bad_value(const char *item_name, Item *bad_item) { char buff[120]; String str(buff,(uint32) sizeof(buff), system_charset_info); - String *str2= bad_item->fixed? bad_item->val_str(&str):NULL; + String *str2= bad_item->is_fixed() ? bad_item->val_str(&str) : NULL; my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL"); } diff --git a/sql/events.cc b/sql/events.cc index c3a578f1097..196c8df591d 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -336,7 +336,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data) if (check_access(thd, EVENT_ACL, parse_data->dbname.str, NULL, NULL, 0, 0)) DBUG_RETURN(TRUE); - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (lock_object_name(thd, MDL_key::EVENT, parse_data->dbname.str, parse_data->name.str)) @@ -401,7 +401,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data) my_message_sql(ER_STARTUP, "Event Error: An error occurred while creating query " "string, before writing it into binary log.", - MYF(ME_NOREFRESH)); + MYF(ME_ERROR_LOG)); ret= true; } else @@ -419,10 +419,10 @@ Events::create_event(THD *thd, Event_parse_data *parse_data) thd->restore_stmt_binlog_format(save_binlog_format); DBUG_RETURN(ret); - -WSREP_ERROR_LABEL: - DBUG_RETURN(TRUE); - +#ifdef WITH_WSREP +wsrep_error_label: + DBUG_RETURN(true); +#endif } @@ -550,9 +550,10 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, thd->restore_stmt_binlog_format(save_binlog_format); DBUG_RETURN(ret); - -WSREP_ERROR_LABEL: - DBUG_RETURN(TRUE); +#ifdef WITH_WSREP +wsrep_error_label: + DBUG_RETURN(true); +#endif } @@ -617,9 +618,10 @@ Events::drop_event(THD *thd, const LEX_CSTRING *dbname, thd->restore_stmt_binlog_format(save_binlog_format); DBUG_RETURN(ret); - -WSREP_ERROR_LABEL: - DBUG_RETURN(TRUE); +#ifdef WITH_WSREP +wsrep_error_label: + DBUG_RETURN(true); +#endif } @@ -824,12 +826,13 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) */ if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS) { - DBUG_ASSERT(thd->lex->select_lex.db.str); - if (!is_infoschema_db(&thd->lex->select_lex.db) && // There is no events in I_S - check_access(thd, EVENT_ACL, thd->lex->select_lex.db.str, + DBUG_ASSERT(thd->lex->first_select_lex()->db.str); + if (!is_infoschema_db(&thd->lex->first_select_lex()->db) && // There is no events in I_S + check_access(thd, EVENT_ACL, thd->lex->first_select_lex()->db.str, NULL, NULL, 0, 0)) DBUG_RETURN(1); - db= normalize_db_name(thd->lex->select_lex.db.str, db_tmp, sizeof(db_tmp)); + db= normalize_db_name(thd->lex->first_select_lex()->db.str, + db_tmp, sizeof(db_tmp)); } ret= db_repository->fill_schema_events(thd, tables, db); @@ -924,7 +927,7 @@ Events::init(THD *thd, bool opt_noacl_or_bootstrap) my_message(ER_STARTUP, "Event Scheduler: An error occurred when initializing " "system tables. Disabling the Event Scheduler.", - MYF(ME_NOREFRESH)); + MYF(ME_ERROR_LOG)); /* Disable the scheduler since the system tables are not up to date */ opt_event_scheduler= EVENTS_OFF; goto end; @@ -946,7 +949,7 @@ Events::init(THD *thd, bool opt_noacl_or_bootstrap) { my_message_sql(ER_STARTUP, "Event Scheduler: Error while loading from mysql.event table.", - MYF(ME_NOREFRESH)); + MYF(ME_ERROR_LOG)); res= TRUE; /* fatal error: request unireg_abort */ goto end; } @@ -1163,7 +1166,7 @@ Events::load_events_from_db(THD *thd) { my_message_sql(ER_STARTUP, "Event Scheduler: Failed to open table mysql.event", - MYF(ME_NOREFRESH)); + MYF(ME_ERROR_LOG)); DBUG_RETURN(TRUE); } @@ -1189,7 +1192,7 @@ Events::load_events_from_db(THD *thd) "Event Scheduler: " "Error while loading events from mysql.event. " "The table probably contains bad data or is corrupted", - MYF(ME_NOREFRESH)); + MYF(ME_ERROR_LOG)); delete et; goto end; } @@ -1228,9 +1231,9 @@ Events::load_events_from_db(THD *thd) } my_printf_error(ER_STARTUP, "Event Scheduler: Loaded %d event%s", - MYF(ME_NOREFRESH | + MYF(ME_ERROR_LOG | (global_system_variables.log_warnings) ? - ME_JUST_INFO: 0), + ME_NOTE: 0), count, (count == 1) ? "" : "s"); ret= FALSE; diff --git a/sql/field.cc b/sql/field.cc index 4ba31e17fa4..d7214687e2d 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -62,8 +62,12 @@ const char field_separator=','; #define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \ ((ulong) ((1LL << MY_MIN(arg, 4) * 8) - 1)) -#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index))) -#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(is_stat_field || !table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || (table->vcol_set && bitmap_is_set(table->vcol_set, field_index)))) +#define ASSERT_COLUMN_MARKED_FOR_READ \ + DBUG_ASSERT(!table || !table->read_set || \ + bitmap_is_set(table->read_set, field_index)) +#define ASSERT_COLUMN_MARKED_FOR_WRITE \ + DBUG_ASSERT(is_stat_field || !table || !table->write_set || \ + bitmap_is_set(table->write_set, field_index)) #define FLAGSTR(S,F) ((S) & (F) ? #F " " : "") @@ -80,15 +84,14 @@ const int FIELDTYPE_LAST= 254; const int FIELDTYPE_NUM= FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST - FIELDTYPE_TEAR_TO); -static inline int field_type2index (enum_field_types field_type) +static inline int merge_type2index(enum_field_types merge_type) { - DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM || - real_type_to_type(field_type) > FIELDTYPE_TEAR_TO); - DBUG_ASSERT(field_type <= FIELDTYPE_LAST); - field_type= real_type_to_type(field_type); - if (field_type < FIELDTYPE_TEAR_FROM) - return field_type; - return FIELDTYPE_TEAR_FROM + (field_type - FIELDTYPE_TEAR_TO) - 1; + DBUG_ASSERT(merge_type < FIELDTYPE_TEAR_FROM || + merge_type > FIELDTYPE_TEAR_TO); + DBUG_ASSERT(merge_type <= FIELDTYPE_LAST); + if (merge_type < FIELDTYPE_TEAR_FROM) + return merge_type; + return FIELDTYPE_TEAR_FROM + (merge_type - FIELDTYPE_TEAR_TO) - 1; } @@ -913,31 +916,37 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= } }; -/** - Return type of which can carry value of both given types in UNION result. - - @param a type for merging - @param b type for merging - - @return - type of field -*/ - -enum_field_types Field::field_type_merge(enum_field_types a, - enum_field_types b) -{ - return field_types_merge_rules[field_type2index(a)] - [field_type2index(b)]; -} const Type_handler * Type_handler::aggregate_for_result_traditional(const Type_handler *a, const Type_handler *b) { - enum_field_types ta= a->real_field_type(); - enum_field_types tb= b->real_field_type(); - return - Type_handler::get_handler_by_real_type(Field::field_type_merge(ta, tb)); + if (a == b) + { + /* + If two traditional handlers are equal, quickly return "a". + Some handlers (e.g. Type_handler_bool) pretend to be traditional, + but in fact they are not traditional in full extent, they are + only sub-types for now (and don't have a corresponding Field_xxx yet). + Here we preserve such handlers during aggregation. + As a result, COALESCE(true,true) preserves the "boolean" data type. + + Need to do this conversion for deprecated data types, + similar to what field_type_merge_rules[][] does. + */ + switch (a->field_type()) { + case MYSQL_TYPE_DECIMAL: return &type_handler_newdecimal; + case MYSQL_TYPE_DATE: return &type_handler_newdate; + case MYSQL_TYPE_VAR_STRING: return &type_handler_varchar; + default: break; + } + return a; + } + enum_field_types ta= a->traditional_merge_field_type(); + enum_field_types tb= b->traditional_merge_field_type(); + enum_field_types res= field_types_merge_rules[merge_type2index(ta)] + [merge_type2index(tb)]; + return Type_handler::get_handler_by_real_type(res); } @@ -1823,7 +1832,7 @@ int Field::store_timestamp(my_time_t ts, ulong sec_part) { MYSQL_TIME ltime; THD *thd= get_thd(); - thd->timestamp_to_TIME(<ime, ts, sec_part, 0); + thd->timestamp_to_TIME(<ime, ts, sec_part, date_mode_t(0)); return store_time_dec(<ime, decimals()); } @@ -1924,14 +1933,6 @@ Field::unpack(uchar* to, const uchar *from, const uchar *from_end, } -my_decimal *Field::val_decimal(my_decimal *decimal) -{ - /* This never have to be called */ - DBUG_ASSERT(0); - return 0; -} - - void Field_num::add_zerofill_and_unsigned(String &res) const { if (unsigned_flag) @@ -2033,7 +2034,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val, int Field_int::store_decimal(const my_decimal *val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int err= 0; longlong i= convert_decimal2longlong(val, unsigned_flag, &err); return MY_TEST(err | store(i, unsigned_flag)); @@ -2063,17 +2064,16 @@ my_decimal* Field_int::val_decimal(my_decimal *decimal_value) } -bool Field_int::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field_int::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) { ASSERT_COLUMN_MARKED_FOR_READ; - longlong nr= val_int(); - bool neg= !(flags & UNSIGNED_FLAG) && nr < 0; - return int_to_datetime_with_warn(neg, neg ? -nr : nr, ltime, fuzzydate, - field_name.str); + Longlong_hybrid nr(val_int(), (flags & UNSIGNED_FLAG)); + return int_to_datetime_with_warn(get_thd(), nr, ltime, + fuzzydate, field_name.str); } -bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id) +bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id) { ASSERT_COLUMN_MARKED_FOR_READ; DBUG_ASSERT(ltime); @@ -2201,7 +2201,7 @@ void Field_num::make_send_field(Send_field *field) int Field_str::store_decimal(const my_decimal *d) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; double val; /* TODO: use decimal2string? */ int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR & @@ -2253,12 +2253,13 @@ uint Field::fill_cache_field(CACHE_FIELD *copy) } -bool Field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) { char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), + str_to_datetime_with_warn(get_thd(), + res->charset(), res->ptr(), res->length(), ltime, fuzzydate)) return 1; return 0; @@ -2273,7 +2274,7 @@ bool Field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) int Field::store_time_dec(const MYSQL_TIME *ltime, uint dec) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[MAX_DATE_STRING_REP_LENGTH]; uint length= (uint) my_TIME_to_str(ltime, buff, dec); /* Avoid conversion when field character set is ASCII compatible */ @@ -2331,6 +2332,36 @@ Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table, } +/** + Create field for temporary table from given field. + + @param thd Thread handler + @param table Temporary table + @param maybe_null_arg If the result field should be NULL-able, + even if the original field is NOT NULL, e.g. for: + - OUTER JOIN fields + - WITH ROLLUP fields + - arguments of aggregate functions, e.g. SUM(column1) + @retval NULL, on error + @retval pointer to the new field created, on success. +*/ + +Field *Field::create_tmp_field(MEM_ROOT *mem_root, TABLE *new_table, + bool maybe_null_arg) +{ + Field *new_field; + + if ((new_field= make_new_field(mem_root, new_table, new_table == table))) + { + new_field->init_for_tmp_table(this, new_table); + new_field->flags|= flags & NO_DEFAULT_VALUE_FLAG; + if (maybe_null_arg) + new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join + } + return new_field; +} + + /* This is used to generate a field in TABLE from TABLE_SHARE */ Field *Field::clone(MEM_ROOT *root, TABLE *new_table) @@ -2500,7 +2531,7 @@ void Field_decimal::overflow(bool negative) int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[STRING_BUFFER_USUAL_SIZE]; String tmp(buff,sizeof(buff), &my_charset_bin); const uchar *from= (uchar*) from_arg; @@ -2866,7 +2897,7 @@ int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs) int Field_decimal::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; if (unsigned_flag && nr < 0) { overflow(1); @@ -2904,7 +2935,7 @@ int Field_decimal::store(double nr) int Field_decimal::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[22]; uint length, int_part; char fyllchar; @@ -3136,7 +3167,7 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, Otherwise sets maximal number that can be stored in the field. @param decimal_value my_decimal - @param [OUT] native_error the error returned by my_decimal2binary(). + @param [OUT] native_error the error returned by my_decimal::to_binary(). @retval 0 ok @@ -3147,7 +3178,7 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, bool Field_new_decimal::store_value(const my_decimal *decimal_value, int *native_error) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; DBUG_ENTER("Field_new_decimal::store_value"); #ifndef DBUG_OFF @@ -3174,8 +3205,8 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value, } #endif - *native_error= my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, - decimal_value, ptr, precision, dec); + *native_error= decimal_value->to_binary(ptr, precision, dec, + E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW); if (unlikely(*native_error == E_DEC_OVERFLOW)) { @@ -3183,7 +3214,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value, DBUG_PRINT("info", ("overflow")); set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); set_value_on_overflow(&buff, decimal_value->sign()); - my_decimal2binary(E_DEC_FATAL_ERROR, &buff, ptr, precision, dec); + buff.to_binary(ptr, precision, dec); error= 1; } DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr, @@ -3205,7 +3236,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) int Field_new_decimal::store(const char *from, size_t length, CHARSET_INFO *charset_arg) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; my_decimal decimal_value; THD *thd= get_thd(); DBUG_ENTER("Field_new_decimal::store(char*)"); @@ -3289,7 +3320,7 @@ int Field_new_decimal::store(const char *from, size_t length, int Field_new_decimal::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; my_decimal decimal_value; int err; THD *thd= get_thd(); @@ -3314,7 +3345,7 @@ int Field_new_decimal::store(double nr) int Field_new_decimal::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; my_decimal decimal_value; int err; @@ -3336,7 +3367,7 @@ int Field_new_decimal::store(longlong nr, bool unsigned_val) int Field_new_decimal::store_decimal(const my_decimal *decimal_value) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; return store_value(decimal_value); } @@ -3348,37 +3379,6 @@ int Field_new_decimal::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) } -double Field_new_decimal::val_real(void) -{ - ASSERT_COLUMN_MARKED_FOR_READ; - double dbl; - my_decimal decimal_value; - my_decimal2double(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), &dbl); - return dbl; -} - - -longlong Field_new_decimal::val_int(void) -{ - ASSERT_COLUMN_MARKED_FOR_READ; - longlong i; - my_decimal decimal_value; - my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), - unsigned_flag, &i); - return i; -} - - -ulonglong Field_new_decimal::val_uint(void) -{ - ASSERT_COLUMN_MARKED_FOR_READ; - longlong i; - my_decimal decimal_value; - my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), true, &i); - return i; -} - - my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -3391,27 +3391,6 @@ my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value) } -String *Field_new_decimal::val_str(String *val_buffer, - String *val_ptr __attribute__((unused))) -{ - ASSERT_COLUMN_MARKED_FOR_READ; - my_decimal decimal_value; - uint fixed_precision= zerofill ? precision : 0; - my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), - fixed_precision, dec, '0', val_buffer); - val_buffer->set_charset(&my_charset_numeric); - return val_buffer; -} - - -bool Field_new_decimal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) -{ - my_decimal value; - return decimal_to_datetime_with_warn(val_decimal(&value), - ltime, fuzzydate, field_name.str); -} - - int Field_new_decimal::cmp(const uchar *a,const uchar*b) { return memcmp(a, b, bin_size); @@ -3566,8 +3545,8 @@ Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx, if (const_item->field_type() != MYSQL_TYPE_NEWDECIMAL || const_item->decimal_scale() != decimals()) { - my_decimal *val, val_buffer, val_buffer2; - if (!(val= const_item->val_decimal(&val_buffer))) + VDec val(const_item); + if (val.is_null()) { DBUG_ASSERT(0); return const_item; @@ -3577,9 +3556,9 @@ Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx, See comments about truncation in the same place in Field_time::get_equal_const_item(). */ - my_decimal_round(E_DEC_FATAL_ERROR, val, decimals(), true, &val_buffer2); - return new (thd->mem_root) Item_decimal(thd, field_name.str, - &val_buffer2, + my_decimal tmp; + val.round_to(&tmp, decimals(), TRUNCATE); + return new (thd->mem_root) Item_decimal(thd, field_name.str, &tmp, decimals(), field_length); } break; @@ -3605,7 +3584,7 @@ int Field_int::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error; longlong rnd; @@ -3617,7 +3596,7 @@ int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs) int Field_tiny::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; nr=rint(nr); if (unsigned_flag) @@ -3660,7 +3639,7 @@ int Field_tiny::store(double nr) int Field_tiny::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if (unsigned_flag) @@ -3765,7 +3744,7 @@ void Field_tiny::sql_type(String &res) const int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int store_tmp; int error; longlong rnd; @@ -3779,7 +3758,7 @@ int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs) int Field_short::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int16 res; nr=rint(nr); @@ -3824,7 +3803,7 @@ int Field_short::store(double nr) int Field_short::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int16 res; @@ -3939,7 +3918,7 @@ void Field_short::sql_type(String &res) const int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int store_tmp; int error; longlong rnd; @@ -3953,7 +3932,7 @@ int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs) int Field_medium::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; nr=rint(nr); if (unsigned_flag) @@ -3999,7 +3978,7 @@ int Field_medium::store(double nr) int Field_medium::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if (unsigned_flag) @@ -4137,7 +4116,7 @@ void Field_medium::sql_type(String &res) const int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; long store_tmp; int error; longlong rnd; @@ -4151,7 +4130,7 @@ int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs) int Field_long::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int32 res; nr=rint(nr); @@ -4196,7 +4175,7 @@ int Field_long::store(double nr) int Field_long::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int32 res; @@ -4310,7 +4289,7 @@ void Field_long::sql_type(String &res) const int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; char *end; ulonglong tmp; @@ -4333,7 +4312,7 @@ int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs) int Field_longlong::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; Converter_double_to_longlong conv(nr, unsigned_flag); if (unlikely(conv.error())) @@ -4346,7 +4325,7 @@ int Field_longlong::store(double nr) int Field_longlong::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if (unlikely(nr < 0)) // Only possible error @@ -4457,7 +4436,7 @@ void Field_longlong::sql_type(String &res) const void Field_longlong::set_max() { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; set_notnull(); int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX); } @@ -4494,7 +4473,7 @@ int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs) int Field_float::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= truncate_double(&nr, field_length, not_fixed ? NOT_FIXED_DEC : dec, unsigned_flag, FLT_MAX); @@ -4673,7 +4652,7 @@ int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs) int Field_double::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= truncate_double(&nr, field_length, not_fixed ? NOT_FIXED_DEC : dec, unsigned_flag, DBL_MAX); @@ -4819,13 +4798,6 @@ Converter_double_to_longlong::push_warning(THD *thd, } -int Field_real::store_decimal(const my_decimal *dm) -{ - double dbl; - my_decimal2double(E_DEC_FATAL_ERROR, dm, &dbl); - return store(dbl); -} - int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) { return store(TIME_to_double(ltime)); @@ -4858,11 +4830,12 @@ my_decimal *Field_real::val_decimal(my_decimal *decimal_value) } -bool Field_real::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field_real::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) { ASSERT_COLUMN_MARKED_FOR_READ; double nr= val_real(); - return double_to_datetime_with_warn(nr, ltime, fuzzydate, field_name.str); + return double_to_datetime_with_warn(get_thd(), nr, ltime, fuzzydate, + field_name.str); } @@ -5053,119 +5026,103 @@ my_time_t Field_timestamp::get_timestamp(const uchar *pos, } -int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time, - const ErrConv *str, - int was_cut, - bool have_smth_to_conv) +int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt, + const ErrConv *str, int was_cut) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - uint error = 0; - my_time_t timestamp; + ASSERT_COLUMN_MARKED_FOR_WRITE; + static const timeval zero= {0, (uint) 0 }; - if (MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) || !have_smth_to_conv) + // Handle totally bad values + if (!dt->is_valid_datetime()) { - error= 1; - set_datetime_warning(WARN_DATA_TRUNCATED, - str, MYSQL_TIMESTAMP_DATETIME, 1); - } - else if (MYSQL_TIME_WARN_HAVE_NOTES(was_cut)) - { - error= 3; - set_datetime_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, - str, MYSQL_TIMESTAMP_DATETIME, 1); + set_datetime_warning(WARN_DATA_TRUNCATED, str, MYSQL_TIMESTAMP_DATETIME, 1); + store_TIMEVAL(zero); + return 1; } - /* Only convert a correct date (not a zero date) */ - if (have_smth_to_conv && l_time->month) + + // Handle values that do not need DATETIME to TIMESTAMP conversion + if (!dt->get_mysql_time()->month) { - uint conversion_error; - timestamp= TIME_to_timestamp(thd, l_time, &conversion_error); - if (timestamp == 0 && l_time->second_part == 0) - conversion_error= ER_WARN_DATA_OUT_OF_RANGE; - if (unlikely(conversion_error)) - { - set_datetime_warning(conversion_error, - str, MYSQL_TIMESTAMP_DATETIME, !error); - error= 1; - } + /* + Zero date is allowed by the current sql_mode. Store zero timestamp. + Return success or a warning about non-fatal truncation, e.g.: + INSERT INTO t1 (ts) VALUES ('0000-00-00 00:00:00 some tail'); + */ + store_TIMEVAL(zero); + return store_TIME_return_code_with_warnings(was_cut, str, + MYSQL_TIMESTAMP_DATETIME); } - else + + // Convert DATETIME to TIMESTAMP + uint conversion_error; + const MYSQL_TIME *l_time= dt->get_mysql_time(); + my_time_t timestamp= TIME_to_timestamp(thd, l_time, &conversion_error); + if (timestamp == 0 && l_time->second_part == 0) { - timestamp= 0; - l_time->second_part= 0; + set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, MYSQL_TIMESTAMP_DATETIME, 1); + store_TIMEVAL(zero); + return 1; // date was fine but pointed to a DST gap } - store_TIME(timestamp, l_time->second_part); - return error; -} + // Store the value + DBUG_ASSERT(!dt->fraction_remainder(decimals())); + store_TIMEVAL(Timeval(timestamp, l_time->second_part)); -static bool -copy_or_convert_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) -{ - if (from->time_type == MYSQL_TIMESTAMP_TIME) - return time_to_datetime(thd, from, to); - *to= *from; - return false; + // Calculate return value and send warnings if needed + if (unlikely(conversion_error)) // e.g. DATETIME in the DST gap + { + set_datetime_warning(conversion_error, str, MYSQL_TIMESTAMP_DATETIME, 1); + return 1; + } + return store_TIME_return_code_with_warnings(was_cut, str, + MYSQL_TIMESTAMP_DATETIME); } -sql_mode_t Field_timestamp::sql_mode_for_timestamp(THD *thd) const +date_mode_t Field_timestamp::sql_mode_for_timestamp(THD *thd) const { // We don't want to store invalid or fuzzy datetime values in TIMESTAMP - return (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE; + return date_mode_t((thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE); } int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec) { - int unused; + int warn; ErrConvTime str(ltime); THD *thd= get_thd(); - MYSQL_TIME l_time; - bool valid= !copy_or_convert_to_datetime(thd, ltime, &l_time) && - !check_date(&l_time, pack_time(&l_time) != 0, - sql_mode_for_timestamp(thd), &unused); - return store_TIME_with_warning(thd, &l_time, &str, false, valid); + Datetime dt(thd, &warn, ltime, sql_mode_for_timestamp(thd), decimals()); + return store_TIME_with_warning(thd, &dt, &str, warn); } int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs) { - MYSQL_TIME l_time; - MYSQL_TIME_STATUS status; - bool have_smth_to_conv; ErrConvString str(from, len, cs); THD *thd= get_thd(); - - have_smth_to_conv= !str_to_datetime(cs, from, len, &l_time, - sql_mode_for_timestamp(thd), &status); - return store_TIME_with_warning(thd, &l_time, &str, - status.warnings, have_smth_to_conv); + MYSQL_TIME_STATUS st; + Datetime dt(&st, from, len, cs, sql_mode_for_timestamp(thd), decimals()); + return store_TIME_with_warning(thd, &dt, &str, st.warnings); } int Field_timestamp::store(double nr) { - MYSQL_TIME l_time; int error; ErrConvDouble str(nr); THD *thd= get_thd(); - - longlong tmp= double_to_datetime(nr, &l_time, sql_mode_for_timestamp(thd), - &error); - return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1); + Datetime dt(&error, Sec6(nr), sql_mode_for_timestamp(thd), decimals()); + return store_TIME_with_warning(thd, &dt, &str, error); } int Field_timestamp::store(longlong nr, bool unsigned_val) { - MYSQL_TIME l_time; int error; - ErrConvInteger str(nr, unsigned_val); + ErrConvInteger str(Longlong_hybrid(nr, unsigned_val)); THD *thd= get_thd(); - - longlong tmp= number_to_datetime(nr, 0, &l_time, sql_mode_for_timestamp(thd), - &error); - return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1); + Datetime dt(&error, Sec6(nr, unsigned_val), sql_mode_for_timestamp(thd)); + return store_TIME_with_warning(thd, &dt, &str, error); } @@ -5173,7 +5130,7 @@ int Field_timestamp::store_timestamp(my_time_t ts, ulong sec_part) { store_TIME(ts, sec_part); if (ts == 0 && sec_part == 0 && - get_thd()->variables.sql_mode & TIME_NO_ZERO_DATE) + get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE) { ErrConvString s( STRING_WITH_LEN("0000-00-00 00:00:00.000000") - (decimals() ? 6 - decimals() : 7), @@ -5282,11 +5239,11 @@ Field_timestamp::validate_value_in_record(THD *thd, const uchar *record) const DBUG_ASSERT(!is_null_in_record(record)); ulong sec_part; return !get_timestamp(ptr_in_record(record), &sec_part) && !sec_part && - (sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != 0; + bool(sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != false; } -bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_timestamp::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { ulong sec_part; my_time_t ts= get_timestamp(&sec_part); @@ -5297,7 +5254,7 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) bool Field_timestamp::send_binary(Protocol *protocol) { MYSQL_TIME ltime; - Field_timestamp::get_date(<ime, 0); + Field_timestamp::get_date(<ime, date_mode_t(0)); return protocol->store(<ime, 0); } @@ -5427,10 +5384,10 @@ static longlong read_lowendian(const uchar *from, uint bytes) } } -void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part) +void Field_timestamp_hires::store_TIMEVAL(const timeval &tv) { - mi_int4store(ptr, timestamp); - store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes(dec)); + mi_int4store(ptr, tv.tv_sec); + store_bigendian(sec_part_shift(tv.tv_usec, dec), ptr+4, sec_part_bytes(dec)); } my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos, @@ -5455,29 +5412,17 @@ double Field_timestamp_with_dec::val_real(void) my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); return TIME_to_my_decimal(<ime, d); } int Field_timestamp::store_decimal(const my_decimal *d) { - ulonglong nr; - ulong sec_part; int error; - MYSQL_TIME ltime; - longlong tmp; THD *thd= get_thd(); ErrConvDecimal str(d); - - if (my_decimal2seconds(d, &nr, &sec_part)) - { - tmp= -1; - error= 2; - } - else - tmp= number_to_datetime(nr, sec_part, <ime, sql_mode_for_timestamp(thd), - &error); - return store_TIME_with_warning(thd, <ime, &str, error, tmp != -1); + Datetime dt(&error, Sec6(d), sql_mode_for_timestamp(thd), decimals()); + return store_TIME_with_warning(thd, &dt, &str, error); } int Field_timestamp_with_dec::set_time() @@ -5492,7 +5437,7 @@ int Field_timestamp_with_dec::set_time() bool Field_timestamp_with_dec::send_binary(Protocol *protocol) { MYSQL_TIME ltime; - Field_timestamp::get_date(<ime, 0); + Field_timestamp::get_date(<ime, date_mode_t(0)); return protocol->store(<ime, dec); } @@ -5521,19 +5466,15 @@ void Field_timestamp_with_dec::make_send_field(Send_field *field) ** MySQL-5.6 compatible TIMESTAMP(N) **************************************************************/ -void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part) +void Field_timestampf::store_TIMEVAL(const timeval &tm) { - struct timeval tm; - tm.tv_sec= timestamp; - tm.tv_usec= sec_part; - my_timeval_trunc(&tm, dec); my_timestamp_to_binary(&tm, ptr, dec); } void Field_timestampf::set_max() { DBUG_ENTER("Field_timestampf::set_max"); - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS); set_notnull(); @@ -5601,110 +5542,66 @@ void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level, 3 Datetime value that was cut (warning level NOTE) This is used by opt_range.cc:get_mm_leaf(). */ -int Field_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime, - const ErrConv *str, - int was_cut, - int have_smth_to_conv) +int Field_datetime::store_TIME_with_warning(const Datetime *dt, + const ErrConv *str, + int was_cut) { - Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN; - int ret= 2; - - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - - if (was_cut == 0 && have_smth_to_conv == 0) // special case: zero date - { - was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE; - } - else if (!have_smth_to_conv) - { - bzero(ltime, sizeof(*ltime)); - was_cut= MYSQL_TIME_WARN_TRUNCATED; - ret= 1; - } - else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) && - (MYSQL_TIME_WARN_HAVE_NOTES(was_cut) || - (type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_DATE && - (ltime->hour || ltime->minute || ltime->second || ltime->second_part)))) - { - trunc_level= Sql_condition::WARN_LEVEL_NOTE; - was_cut|= MYSQL_TIME_WARN_TRUNCATED; - ret= 3; - } - set_warnings(trunc_level, str, was_cut, - type_handler()->mysql_timestamp_type()); - store_TIME(ltime); - return was_cut ? ret : 0; + ASSERT_COLUMN_MARKED_FOR_WRITE; + // Handle totally bad values + if (!dt->is_valid_datetime()) + return store_invalid_with_warning(str, was_cut, MYSQL_TIMESTAMP_DATETIME); + // Store the value + DBUG_ASSERT(!dt->fraction_remainder(decimals())); + store_TIME(dt->get_mysql_time()); + // Caclulate return value and send warnings if needed + return store_TIME_return_code_with_warnings(was_cut, str, + MYSQL_TIMESTAMP_DATETIME); } -int Field_temporal_with_date::store(const char *from, size_t len, CHARSET_INFO *cs) +int Field_datetime::store(const char *from, size_t len, CHARSET_INFO *cs) { - MYSQL_TIME ltime; - MYSQL_TIME_STATUS status; - THD *thd= get_thd(); + MYSQL_TIME_STATUS st; ErrConvString str(from, len, cs); - bool func_res= !str_to_datetime(cs, from, len, <ime, - sql_mode_for_dates(thd), - &status); - return store_TIME_with_warning(<ime, &str, status.warnings, func_res); + Datetime dt(&st, from, len, cs, sql_mode_for_dates(get_thd()), decimals()); + return store_TIME_with_warning(&dt, &str, st.warnings); } - -int Field_temporal_with_date::store(double nr) +int Field_datetime::store(double nr) { - int error= 0; - MYSQL_TIME ltime; - THD *thd= get_thd(); + int error; ErrConvDouble str(nr); - - longlong tmp= double_to_datetime(nr, <ime, - (uint) sql_mode_for_dates(thd), &error); - return store_TIME_with_warning(<ime, &str, error, tmp != -1); + Datetime dt(&error, Sec6(nr), sql_mode_for_dates(get_thd()), decimals()); + return store_TIME_with_warning(&dt, &str, error); } -int Field_temporal_with_date::store(longlong nr, bool unsigned_val) +int Field_datetime::store(longlong nr, bool unsigned_val) { int error; - MYSQL_TIME ltime; - longlong tmp; - THD *thd= get_thd(); - ErrConvInteger str(nr, unsigned_val); - - tmp= number_to_datetime(nr, 0, <ime, sql_mode_for_dates(thd), &error); - - return store_TIME_with_warning(<ime, &str, error, tmp != -1); + ErrConvInteger str(Longlong_hybrid(nr, unsigned_val)); + Datetime dt(&error, Sec6(nr, unsigned_val), sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, error); } - -int Field_temporal_with_date::store_time_dec(const MYSQL_TIME *ltime, uint dec) +int Field_datetime::store_time_dec(const MYSQL_TIME *ltime, uint dec) { - int error= 0, have_smth_to_conv= 1; + int error; ErrConvTime str(ltime); - MYSQL_TIME l_time; - - if (copy_or_convert_to_datetime(get_thd(), ltime, &l_time)) - { - /* - Set have_smth_to_conv and error in a way to have - store_TIME_with_warning do bzero(). - */ - have_smth_to_conv= false; - error= MYSQL_TIME_WARN_OUT_OF_RANGE; - } - else - { - /* - We don't perform range checking here since values stored in TIME - structure always fit into DATETIME range. - */ - have_smth_to_conv= !check_date(&l_time, pack_time(&l_time) != 0, - sql_mode_for_dates(get_thd()), &error); - } - return store_TIME_with_warning(&l_time, &str, error, have_smth_to_conv); + THD *thd= get_thd(); + Datetime dt(thd, &error, ltime, sql_mode_for_dates(thd), decimals()); + return store_TIME_with_warning(&dt, &str, error); } +int Field_datetime::store_decimal(const my_decimal *d) +{ + int error; + ErrConvDecimal str(d); + Datetime tm(&error, Sec6(d), sql_mode_for_dates(get_thd()), decimals()); + return store_TIME_with_warning(&tm, &str, error); +} + bool Field_temporal_with_date::validate_value_in_record(THD *thd, const uchar *record) const @@ -5718,7 +5615,7 @@ Field_temporal_with_date::validate_value_in_record(THD *thd, my_decimal *Field_temporal::val_decimal(my_decimal *d) { MYSQL_TIME ltime; - if (get_date(<ime, 0)) + if (get_date(<ime, date_mode_t(0))) { bzero(<ime, sizeof(ltime)); ltime.time_type= type_handler()->mysql_timestamp_type(); @@ -5751,7 +5648,7 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, const_item->field_type() != MYSQL_TYPE_TIMESTAMP) || const_item->decimals != decimals()) { - Datetime dt(thd, const_item, 0); + Datetime dt(thd, const_item, date_mode_t(0)); if (!dt.is_valid_datetime()) return NULL; /* @@ -5766,7 +5663,7 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, case ANY_SUBST: if (!is_temporal_type_with_date(const_item->field_type())) { - Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES); + Datetime dt(thd, const_item, Datetime::comparison_flags_for_get_date()); if (!dt.is_valid_datetime()) return NULL; return new (thd->mem_root) @@ -5787,36 +5684,18 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, ** In number context: HHMMSS ** Stored as a 3 byte unsigned int ****************************************************************************/ -int Field_time::store_TIME_with_warning(MYSQL_TIME *ltime, - const ErrConv *str, - int was_cut, - int have_smth_to_conv) +int Field_time::store_TIME_with_warning(const Time *t, + const ErrConv *str, int warn) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - - if (!have_smth_to_conv) - { - bzero(ltime, sizeof(*ltime)); - store_TIME(ltime); - set_warnings(Sql_condition::WARN_LEVEL_WARN, str, MYSQL_TIME_WARN_TRUNCATED); - return 1; - } - if (ltime->year != 0 || ltime->month != 0) - { - ltime->year= ltime->month= ltime->day= 0; - was_cut|= MYSQL_TIME_NOTE_TRUNCATED; - } - my_time_trunc(ltime, decimals()); - store_TIME(ltime); - if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) && - MYSQL_TIME_WARN_HAVE_NOTES(was_cut)) - { - set_warnings(Sql_condition::WARN_LEVEL_NOTE, str, - was_cut | MYSQL_TIME_WARN_TRUNCATED); - return 3; - } - set_warnings(Sql_condition::WARN_LEVEL_WARN, str, was_cut); - return was_cut ? 2 : 0; + ASSERT_COLUMN_MARKED_FOR_WRITE; + // Handle totally bad values + if (!t->is_valid_time()) + return store_invalid_with_warning(str, warn, MYSQL_TIMESTAMP_TIME); + // Store the value + DBUG_ASSERT(!t->fraction_remainder(decimals())); + store_TIME(t->get_mysql_time()); + // Calculate return value and send warnings if needed + return store_TIME_return_code_with_warnings(warn, str, MYSQL_TIMESTAMP_TIME); } @@ -5833,88 +5712,39 @@ void Field_time::store_TIME(const MYSQL_TIME *ltime) int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs) { - MYSQL_TIME ltime; - MYSQL_TIME_STATUS status; ErrConvString str(from, len, cs); - bool have_smth_to_conv= - !str_to_time(cs, from, len, <ime, sql_mode_for_dates(get_thd()), - &status); - - return store_TIME_with_warning(<ime, &str, - status.warnings, have_smth_to_conv); -} - - -/** - subtract a given number of days from DATETIME, return TIME - - optimized version of calc_time_diff() - - @note it might generate TIME values outside of the valid TIME range! -*/ -static void calc_datetime_days_diff(MYSQL_TIME *ltime, long days) -{ - long daydiff= calc_daynr(ltime->year, ltime->month, ltime->day) - days; - ltime->year= ltime->month= 0; - if (daydiff >=0 ) - { - ltime->day= daydiff; - ltime->time_type= MYSQL_TIMESTAMP_TIME; - } - else - { - longlong timediff= ((((daydiff * 24LL + - ltime->hour) * 60LL + - ltime->minute) * 60LL + - ltime->second) * 1000000LL + - ltime->second_part); - unpack_time(timediff, ltime, MYSQL_TIMESTAMP_TIME); - } + MYSQL_TIME_STATUS st; + THD *thd= get_thd(); + Time tm(thd, &st, from, len, cs, sql_mode_for_dates(thd), decimals()); + return store_TIME_with_warning(&tm, &str, st.warnings); } int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec) { - MYSQL_TIME l_time= *ltime; ErrConvTime str(ltime); - int was_cut= 0; - - if (curdays && l_time.time_type != MYSQL_TIMESTAMP_TIME) - calc_datetime_days_diff(&l_time, curdays); - - int have_smth_to_conv= !check_time_range(&l_time, decimals(), &was_cut); - return store_TIME_with_warning(&l_time, &str, was_cut, have_smth_to_conv); + int warn; + Time tm(&warn, ltime, curdays, decimals()); + return store_TIME_with_warning(&tm, &str, warn); } int Field_time::store(double nr) { - MYSQL_TIME ltime; ErrConvDouble str(nr); int was_cut; - bool neg= nr < 0; - if (neg) - nr= -nr; - int have_smth_to_conv= !number_to_time(neg, (ulonglong) nr, - (ulong)((nr - floor(nr)) * TIME_SECOND_PART_FACTOR), - <ime, &was_cut); - - return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); + Time tm(get_thd(), &was_cut, Sec6(nr), decimals()); + return store_TIME_with_warning(&tm, &str, was_cut); } int Field_time::store(longlong nr, bool unsigned_val) { - MYSQL_TIME ltime; - ErrConvInteger str(nr, unsigned_val); + ErrConvInteger str(Longlong_hybrid(nr, unsigned_val)); int was_cut; - if (nr < 0 && unsigned_val) - nr= 99991231235959LL + 1; - int have_smth_to_conv= !number_to_time(nr < 0, - (ulonglong) (nr < 0 ? -nr : nr), - 0, <ime, &was_cut); - - return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); + // Need fractional digit truncation if nr overflows to '838:59:59.999999' + Time tm(get_thd(), &was_cut, Sec6(nr, unsigned_val), decimals()); + return store_TIME_with_warning(&tm, &str, was_cut); } @@ -5973,7 +5803,7 @@ String *Field_time::val_str(String *str, } -bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate) +bool Field_time::check_zero_in_date_with_warn(date_mode_t fuzzydate) { if (!(fuzzydate & TIME_TIME_ONLY) && (fuzzydate & TIME_NO_ZERO_IN_DATE)) { @@ -5995,7 +5825,7 @@ bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate) DATE_FORMAT(time, "%l.%i %p") */ -bool Field_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_time::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (check_zero_in_date_with_warn(fuzzydate)) return true; @@ -6069,16 +5899,10 @@ void Field_time_hires::store_TIME(const MYSQL_TIME *ltime) int Field_time::store_decimal(const my_decimal *d) { - ulonglong nr; - ulong sec_part; ErrConvDecimal str(d); - MYSQL_TIME ltime; int was_cut; - bool neg= my_decimal2seconds(d, &nr, &sec_part); - - int have_smth_to_conv= !number_to_time(neg, nr, sec_part, <ime, &was_cut); - - return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); + Time tm(get_thd(), &was_cut, Sec6(d), decimals()); + return store_TIME_with_warning(&tm, &str, was_cut); } @@ -6118,14 +5942,28 @@ bool Field_time::can_be_substituted_to_equal_item(const Context &ctx, Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, Item *const_item) { + /* + Old mode conversion from DATETIME with non-zero YYYYMMDD part + to TIME works very inconsistently. Possible variants: + - truncate the YYYYMMDD part + - add (MM*33+DD)*24 to hours + - add (MM*31+DD)*24 to hours + Let's disallow propagation of DATETIME with non-zero YYYYMMDD + as an equal constant for a TIME field. + */ + Time::datetime_to_time_mode_t mode= + (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) ? + Time::DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY : + Time::DATETIME_TO_TIME_MINUS_CURRENT_DATE; + switch (ctx.subst_constraint()) { case ANY_SUBST: if (const_item->field_type() != MYSQL_TYPE_TIME) { - MYSQL_TIME ltime; // Get the value of const_item with conversion from DATETIME to TIME - ulonglong fuzzydate= Time::comparison_flags_for_get_date(); - if (const_item->get_time_with_conversion(thd, <ime, fuzzydate)) + Time tm(get_thd(), const_item, + Time::Options(Time::comparison_flags_for_get_date(), mode)); + if (!tm.is_valid_time()) return NULL; /* Replace a DATE/DATETIME constant to a TIME constant: @@ -6137,8 +5975,9 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, (assuming CURRENT_DATE is '2015-08-30' */ - return new (thd->mem_root) Item_time_literal(thd, <ime, - ltime.second_part ? + return new (thd->mem_root) Item_time_literal(thd, tm.get_mysql_time(), + tm.get_mysql_time()-> + second_part ? TIME_SECOND_PART_DIGITS : 0); } @@ -6147,8 +5986,9 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, if (const_item->field_type() != MYSQL_TYPE_TIME || const_item->decimals != decimals()) { - MYSQL_TIME ltime; - if (const_item->get_time_with_conversion(thd, <ime, TIME_TIME_ONLY)) + Time tm(get_thd(), const_item, + Time::Options(TIME_TIME_ONLY, mode)); + if (!tm.is_valid_time()) return NULL; /* Note, the value returned in "ltime" can have more fractional @@ -6164,7 +6004,8 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, The optimized WHERE will return with "Impossible WHERE", without having to do the full table scan. */ - return new (thd->mem_root) Item_time_literal(thd, <ime, decimals()); + return new (thd->mem_root) Item_time_literal(thd, tm.get_mysql_time(), + decimals()); } break; } @@ -6189,7 +6030,7 @@ double Field_time_with_dec::val_real(void) return TIME_to_double(<ime); } -bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_time_hires::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (check_zero_in_date_with_warn(fuzzydate)) return true; @@ -6241,7 +6082,7 @@ void Field_timef::store_TIME(const MYSQL_TIME *ltime) my_time_packed_to_binary(tmp, ptr, dec); } -bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_timef::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (check_zero_in_date_with_warn(fuzzydate)) return true; @@ -6258,7 +6099,7 @@ bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) int Field_year::store(const char *from, size_t len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; char *end; int error; longlong nr= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error); @@ -6306,7 +6147,7 @@ int Field_year::store(double nr) int Field_year::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155) { *ptr= 0; @@ -6375,12 +6216,13 @@ String *Field_year::val_str(String *val_buffer, } -bool Field_year::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field_year::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) { int tmp= (int) ptr[0]; if (tmp || field_length != 4) tmp+= 1900; - return int_to_datetime_with_warn(false, tmp * 10000, + return int_to_datetime_with_warn(get_thd(), + Longlong_hybrid(tmp * 10000, true), ltime, fuzzydate, field_name.str); } @@ -6393,6 +6235,67 @@ void Field_year::sql_type(String &res) const } +/*****************************************************************************/ + +int Field_date_common::store_TIME_with_warning(const Datetime *dt, + const ErrConv *str, + int was_cut) +{ + ASSERT_COLUMN_MARKED_FOR_WRITE; + // Handle totally bad values + if (!dt->is_valid_datetime()) + return store_invalid_with_warning(str, was_cut, MYSQL_TIMESTAMP_DATE); + // Store the value + if (!dt->hhmmssff_is_zero()) + was_cut|= MYSQL_TIME_NOTE_TRUNCATED; + store_TIME(dt->get_mysql_time()); + // Caclulate return value and send warnings if needed + return store_TIME_return_code_with_warnings(was_cut, str, + MYSQL_TIMESTAMP_DATE); +} + +int Field_date_common::store(const char *from, size_t len, CHARSET_INFO *cs) +{ + MYSQL_TIME_STATUS st; + ErrConvString str(from, len, cs); + Datetime dt(&st, from, len, cs, sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, st.warnings); +} + +int Field_date_common::store(double nr) +{ + int error; + ErrConvDouble str(nr); + Datetime dt(&error, Sec6(nr), sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_date_common::store(longlong nr, bool unsigned_val) +{ + int error; + ErrConvInteger str(Longlong_hybrid(nr, unsigned_val)); + Datetime dt(&error, Sec6(nr, unsigned_val), sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_date_common::store_time_dec(const MYSQL_TIME *ltime, uint dec) +{ + int error; + ErrConvTime str(ltime); + THD *thd= get_thd(); + Datetime dt(thd, &error, ltime, sql_mode_for_dates(thd)); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_date_common::store_decimal(const my_decimal *d) +{ + int error; + ErrConvDecimal str(d); + Datetime tm(&error, Sec6(d), sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&tm, &str, error); +} + + /**************************************************************************** ** date type ** In string context: YYYY-MM-DD @@ -6400,7 +6303,7 @@ void Field_year::sql_type(String &res) const ** Stored as a 4 byte unsigned int ****************************************************************************/ -void Field_date::store_TIME(MYSQL_TIME *ltime) +void Field_date::store_TIME(const MYSQL_TIME *ltime) { uint tmp= ltime->year*10000L + ltime->month*100+ltime->day; int4store(ptr,tmp); @@ -6436,7 +6339,7 @@ longlong Field_date::val_int(void) bool Field_date::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; int32 tmp= sint4korr(pos); @@ -6453,7 +6356,7 @@ String *Field_date::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { MYSQL_TIME ltime; - get_TIME(<ime, ptr, 0); + get_TIME(<ime, ptr, date_mode_t(0)); val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); uint length= (uint) my_date_to_str(<ime, const_cast<char*>(val_buffer->ptr())); @@ -6493,7 +6396,7 @@ void Field_date::sql_type(String &res) const ** In number context: YYYYMMDD ****************************************************************************/ -void Field_newdate::store_TIME(MYSQL_TIME *ltime) +void Field_newdate::store_TIME(const MYSQL_TIME *ltime) { uint tmp= ltime->year*16*32 + ltime->month*32+ltime->day; int3store(ptr,tmp); @@ -6503,7 +6406,7 @@ void Field_newdate::store_TIME(MYSQL_TIME *ltime) bool Field_newdate::send_binary(Protocol *protocol) { MYSQL_TIME tm; - Field_newdate::get_date(&tm,0); + Field_newdate::get_date(&tm, date_mode_t(0)); return protocol->store_date(&tm); } @@ -6555,7 +6458,7 @@ String *Field_newdate::val_str(String *val_buffer, bool Field_newdate::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; uint32 tmp=(uint32) uint3korr(pos); @@ -6599,7 +6502,7 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, if (!is_temporal_type_with_date(const_item->field_type())) { // Get the value of const_item with conversion from TIME to DATETIME - Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES); + Datetime dt(thd, const_item, Datetime::comparison_flags_for_get_date()); if (!dt.is_valid_datetime()) return NULL; /* @@ -6626,7 +6529,7 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, case IDENTITY_SUBST: if (const_item->field_type() != MYSQL_TYPE_DATE) { - Date d(thd, const_item, 0); + Date d(thd, const_item, date_mode_t(0)); if (!d.is_valid_date()) return NULL; return new (thd->mem_root) Item_date_literal(thd, d.get_mysql_time()); @@ -6644,7 +6547,7 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, ** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int. ****************************************************************************/ -void Field_datetime::store_TIME(MYSQL_TIME *ltime) +void Field_datetime::store_TIME(const MYSQL_TIME *ltime) { ulonglong tmp= TIME_to_ulonglong_datetime(ltime); int8store(ptr,tmp); @@ -6653,7 +6556,7 @@ void Field_datetime::store_TIME(MYSQL_TIME *ltime) bool Field_datetime::send_binary(Protocol *protocol) { MYSQL_TIME tm; - Field_datetime::get_date(&tm, 0); + Field_datetime::get_date(&tm, date_mode_t(0)); return protocol->store(&tm, 0); } @@ -6719,7 +6622,7 @@ String *Field_datetime::val_str(String *val_buffer, } bool Field_datetime::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; longlong tmp= sint8korr(pos); @@ -6782,44 +6685,23 @@ int Field_datetime::set_time() thd->variables.time_zone->gmt_sec_to_TIME(&now_time, thd->query_start()); now_time.second_part= thd->query_start_sec_part(); set_notnull(); + my_time_trunc(&now_time, decimals()); store_TIME(&now_time); thd->time_zone_used= 1; return 0; } -void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime) +void Field_datetime_hires::store_TIME(const MYSQL_TIME *ltime) { ulonglong packed= sec_part_shift(pack_time(ltime), dec); store_bigendian(packed, ptr, Field_datetime_hires::pack_length()); } -int Field_temporal_with_date::store_decimal(const my_decimal *d) -{ - ulonglong nr; - ulong sec_part; - int error; - MYSQL_TIME ltime; - longlong tmp; - THD *thd= get_thd(); - ErrConvDecimal str(d); - - if (my_decimal2seconds(d, &nr, &sec_part)) - { - tmp= -1; - error= 2; - } - else - tmp= number_to_datetime(nr, sec_part, <ime, sql_mode_for_dates(thd), - &error); - - return store_TIME_with_warning(<ime, &str, error, tmp != -1); -} - bool Field_datetime_with_dec::send_binary(Protocol *protocol) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); return protocol->store(<ime, dec); } @@ -6827,14 +6709,14 @@ bool Field_datetime_with_dec::send_binary(Protocol *protocol) double Field_datetime_with_dec::val_real(void) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); return TIME_to_double(<ime); } longlong Field_datetime_with_dec::val_int(void) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); return TIME_to_ulonglong_datetime(<ime); } @@ -6843,7 +6725,7 @@ String *Field_datetime_with_dec::val_str(String *str, String *unused __attribute__((unused))) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); str->alloc(field_length+1); str->length(field_length); my_datetime_to_str(<ime, (char*) str->ptr(), dec); @@ -6853,7 +6735,7 @@ String *Field_datetime_with_dec::val_str(String *str, bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; ulonglong packed= read_bigendian(pos, Field_datetime_hires::pack_length()); @@ -6886,15 +6768,14 @@ int Field_datetimef::reset() return 0; } -void Field_datetimef::store_TIME(MYSQL_TIME *ltime) +void Field_datetimef::store_TIME(const MYSQL_TIME *ltime) { - my_time_trunc(ltime, decimals()); longlong tmp= TIME_to_longlong_datetime_packed(ltime); my_datetime_packed_to_binary(tmp, ptr, dec); } bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; longlong tmp= my_datetime_packed_from_binary(pos, dec); @@ -6998,7 +6879,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end, int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; uint copy_length; int rc; @@ -7044,7 +6925,7 @@ int Field_str::store(longlong nr, bool unsigned_val) int Field_str::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; uint local_char_length= MY_MIN(sizeof(buff), field_length / field_charset->mbmaxlen); @@ -7081,9 +6962,8 @@ uint Field_str::is_equal(Create_field *new_field) int Field_longstr::store_decimal(const my_decimal *d) { - char buff[DECIMAL_MAX_STR_LENGTH+1]; - String str(buff, sizeof(buff), &my_charset_numeric); - my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str); + StringBuffer<DECIMAL_MAX_STR_LENGTH+1> str; + d->to_string(&str); return store(str.ptr(), str.length(), str.charset()); } @@ -7300,11 +7180,12 @@ void Field_string::sql_type(String &res) const size_t length; length= cs->cset->snprintf(cs,(char*) res.ptr(), - res.alloced_length(), "%s(%d)", + res.alloced_length(), "%s(%d)%s", (type() == MYSQL_TYPE_VAR_STRING ? (has_charset() ? "varchar" : "varbinary") : (has_charset() ? "char" : "binary")), - (int) field_length / charset()->mbmaxlen); + (int) field_length / charset()->mbmaxlen, + type() == MYSQL_TYPE_VAR_STRING ? "/*old*/" : ""); res.length(length); if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && has_charset() && (charset()->state & MY_CS_BINSORT)) @@ -7541,7 +7422,7 @@ int Field_varstring::save_field_metadata(uchar *metadata_ptr) int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; uint copy_length; int rc; @@ -8078,7 +7959,7 @@ String *Field_longstr::uncompress(String *val_buffer, String *val_ptr, int Field_varstring_compressed::store(const char *from, size_t length, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; uint compressed_length; int rc= compress((char*) get_data(), field_length, from, (uint) length, Field_varstring_compressed::max_display_length(), @@ -8210,7 +8091,7 @@ int Field_blob::copy_value(Field_blob *from) int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; size_t copy_length, new_length; uint copy_len; char *tmp; @@ -8698,7 +8579,7 @@ uint Field_blob::is_equal(Create_field *new_field) int Field_blob_compressed::store(const char *from, size_t length, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; uint compressed_length; uint max_length= max_data_length(); uint to_length= (uint) MY_MIN(max_length, @@ -9041,7 +8922,7 @@ void Field_enum::store_type(ulonglong value) int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int err= 0; char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); @@ -9093,7 +8974,7 @@ int Field_enum::store(double nr) int Field_enum::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if ((ulonglong) nr > typelib->count || nr == 0) { @@ -9224,7 +9105,7 @@ Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table, int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; bool got_warning= 0; int err= 0; char *not_used; @@ -9264,7 +9145,7 @@ int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs) int Field_set::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; ulonglong max_nr; @@ -9643,7 +9524,7 @@ uint Field_bit::is_equal(Create_field *new_field) int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int delta; for (; length && !*from; from++, length--) // skip left 0's @@ -10079,7 +9960,7 @@ Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, int Field_bit_as_char::store(const char *from, size_t length, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + ASSERT_COLUMN_MARKED_FOR_WRITE; int delta; uchar bits= (uchar) (field_length & 7); @@ -10570,322 +10451,90 @@ uint pack_length_to_packflag(uint type) } -Field *make_field(TABLE_SHARE *share, - MEM_ROOT *mem_root, - uchar *ptr, uint32 field_length, - uchar *null_pos, uchar null_bit, - uint pack_flag, - const Type_handler *handler, - CHARSET_INFO *field_charset, - Field::geometry_type geom_type, uint srid, - Field::utype unireg_check, - TYPELIB *interval, - const LEX_CSTRING *field_name, - uint32 flags) +uint Column_definition_attributes::pack_flag_to_pack_length() const { - uchar *UNINIT_VAR(bit_ptr); - uchar UNINIT_VAR(bit_offset); + uint type= f_packtype(pack_flag); // 0..15 + DBUG_ASSERT(type < 16); + switch (type) { + case MYSQL_TYPE_TINY: return 1; + case MYSQL_TYPE_SHORT: return 2; + case MYSQL_TYPE_LONG: return 4; + case MYSQL_TYPE_LONGLONG: return 8; + case MYSQL_TYPE_INT24: return 3; + } + return 0; // This should not happen +} + +Field *Column_definition_attributes::make_field(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const Record_addr *rec, + const Type_handler *handler, + const LEX_CSTRING *field_name, + uint32 flags) + const +{ + DBUG_ASSERT(length <= UINT_MAX32); DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s", - handler->name().ptr(), field_length, interval, + handler->name().ptr(), (uint) length, interval, FLAGSTR(pack_flag, FIELDFLAG_BINARY), FLAGSTR(pack_flag, FIELDFLAG_INTERVAL), FLAGSTR(pack_flag, FIELDFLAG_NUMBER), FLAGSTR(pack_flag, FIELDFLAG_PACK), FLAGSTR(pack_flag, FIELDFLAG_BLOB))); - if (handler == &type_handler_row) - { - DBUG_ASSERT(field_length == 0); - DBUG_ASSERT(f_maybe_null(pack_flag)); - return new (mem_root) Field_row(ptr, field_name); - } - - if (handler->real_field_type() == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) - { - bit_ptr= null_pos; - bit_offset= null_bit; - if (f_maybe_null(pack_flag)) // if null field - { - bit_ptr+= (null_bit == 7); // shift bit_ptr and bit_offset - bit_offset= (bit_offset + 1) & 7; - } - } - - if (!f_maybe_null(pack_flag)) - { - null_pos=0; - null_bit=0; - } - else - { - null_bit= ((uchar) 1) << null_bit; - } - - - if (f_is_alpha(pack_flag)) - { - if (!f_is_packed(pack_flag)) - { - enum_field_types field_type= handler->real_field_type(); - if (field_type == MYSQL_TYPE_STRING || - field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string - field_type == MYSQL_TYPE_VAR_STRING) - return new (mem_root) - Field_string(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - field_charset); - if (field_type == MYSQL_TYPE_VARCHAR) - { - if (unireg_check == Field::TMYSQL_COMPRESSED) - return new (mem_root) - Field_varstring_compressed( - ptr, field_length, - HA_VARCHAR_PACKLENGTH(field_length), - null_pos, null_bit, - unireg_check, field_name, - share, field_charset, zlib_compression_method); - - return new (mem_root) - Field_varstring(ptr,field_length, - HA_VARCHAR_PACKLENGTH(field_length), - null_pos,null_bit, - unireg_check, field_name, - share, - field_charset); - } - return 0; // Error - } - - // MYSQL_TYPE_VAR_STRING is handled above - DBUG_ASSERT(f_packtype(pack_flag) != MYSQL_TYPE_VAR_STRING); - const Type_handler *tmp; - tmp= Type_handler::get_handler_by_real_type((enum_field_types) - f_packtype(pack_flag)); - uint pack_length= tmp->calc_pack_length(field_length); - -#ifdef HAVE_SPATIAL - if (f_is_geom(pack_flag)) - { - status_var_increment(current_thd->status_var.feature_gis); - return new (mem_root) - Field_geom(ptr,null_pos,null_bit, - unireg_check, field_name, share, - pack_length, geom_type, srid); - } -#endif - if (f_is_blob(pack_flag)) - { - if (unireg_check == Field::TMYSQL_COMPRESSED) - return new (mem_root) - Field_blob_compressed(ptr, null_pos, null_bit, - unireg_check, field_name, share, - pack_length, field_charset, zlib_compression_method); - - return new (mem_root) - Field_blob(ptr,null_pos,null_bit, - unireg_check, field_name, share, - pack_length, field_charset); - } - if (interval) - { - if (f_is_enum(pack_flag)) - return new (mem_root) - Field_enum(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - pack_length, interval, field_charset); - else - return new (mem_root) - Field_set(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - pack_length, interval, field_charset); - } - } - - switch (handler->real_field_type()) { - case MYSQL_TYPE_DECIMAL: - return new (mem_root) - Field_decimal(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_decimals(pack_flag), - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); - case MYSQL_TYPE_NEWDECIMAL: - return new (mem_root) - Field_new_decimal(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_decimals(pack_flag), - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); - case MYSQL_TYPE_FLOAT: - { - int decimals= f_decimals(pack_flag); - if (decimals == FLOATING_POINT_DECIMALS) - decimals= NOT_FIXED_DEC; - return new (mem_root) - Field_float(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - decimals, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag)== 0); - } - case MYSQL_TYPE_DOUBLE: - { - int decimals= f_decimals(pack_flag); - if (decimals == FLOATING_POINT_DECIMALS) - decimals= NOT_FIXED_DEC; - return new (mem_root) - Field_double(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - decimals, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag)== 0); - } - case MYSQL_TYPE_TINY: - return new (mem_root) - Field_tiny(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); - case MYSQL_TYPE_SHORT: - return new (mem_root) - Field_short(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); - case MYSQL_TYPE_INT24: - return new (mem_root) - Field_medium(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); - case MYSQL_TYPE_LONG: - return new (mem_root) - Field_long(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); - case MYSQL_TYPE_LONGLONG: - if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG)) - { - return new (mem_root) - Field_vers_trx_id(ptr, field_length, null_pos, null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); - } - else - { - return new (mem_root) - Field_longlong(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); - } - case MYSQL_TYPE_TIMESTAMP: - { - uint dec= field_length > MAX_DATETIME_WIDTH ? - field_length - MAX_DATETIME_WIDTH - 1: 0; - return new_Field_timestamp(mem_root, ptr, null_pos, null_bit, unireg_check, - field_name, share, dec); - } - case MYSQL_TYPE_TIMESTAMP2: - { - uint dec= field_length > MAX_DATETIME_WIDTH ? - field_length - MAX_DATETIME_WIDTH - 1: 0; - return new (mem_root) - Field_timestampf(ptr, null_pos, null_bit, unireg_check, - field_name, share, dec); - } - case MYSQL_TYPE_YEAR: - return new (mem_root) - Field_year(ptr,field_length,null_pos,null_bit, - unireg_check, field_name); - case MYSQL_TYPE_DATE: - return new (mem_root) - Field_date(ptr,null_pos,null_bit, - unireg_check, field_name); - case MYSQL_TYPE_NEWDATE: - return new (mem_root) - Field_newdate(ptr,null_pos,null_bit, - unireg_check, field_name); - case MYSQL_TYPE_TIME: - { - uint dec= field_length > MIN_TIME_WIDTH ? - field_length - MIN_TIME_WIDTH - 1: 0; - return new_Field_time(mem_root, ptr, null_pos, null_bit, unireg_check, - field_name, dec); - } - case MYSQL_TYPE_TIME2: - { - uint dec= field_length > MIN_TIME_WIDTH ? - field_length - MIN_TIME_WIDTH - 1: 0; - return new (mem_root) - Field_timef(ptr, null_pos, null_bit, unireg_check, - field_name, dec); - } - case MYSQL_TYPE_DATETIME: - { - uint dec= field_length > MAX_DATETIME_WIDTH ? - field_length - MAX_DATETIME_WIDTH - 1: 0; - return new_Field_datetime(mem_root, ptr, null_pos, null_bit, unireg_check, - field_name, dec); - } - case MYSQL_TYPE_DATETIME2: - { - uint dec= field_length > MAX_DATETIME_WIDTH ? - field_length - MAX_DATETIME_WIDTH - 1: 0; - return new (mem_root) - Field_datetimef(ptr, null_pos, null_bit, unireg_check, - field_name, dec); - } - case MYSQL_TYPE_NULL: - return new (mem_root) - Field_null(ptr, field_length, unireg_check, field_name, - field_charset); - case MYSQL_TYPE_BIT: - return (f_bit_as_char(pack_flag) ? - new (mem_root) - Field_bit_as_char(ptr, field_length, null_pos, null_bit, - unireg_check, field_name) : - new (mem_root) - Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr, - bit_offset, unireg_check, field_name)); - - default: // Impossible (Wrong version) - break; - } - return 0; + Record_addr addr(rec->ptr(), f_maybe_null(pack_flag) ? rec->null() : + Bit_addr()); + /* + Special code for the BIT-alike data types + who store data bits together with NULL-bits. + */ + Bit_addr bit(rec->null()); + if (f_maybe_null(pack_flag)) + bit.inc(); + return handler->make_table_field_from_def(share, mem_root, field_name, + addr, bit, this, flags); } + bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const { - return item->type() == Item::DATE_ITEM; + return item->is_of_type(Item::CONST_ITEM, TIME_RESULT); } +Column_definition_attributes::Column_definition_attributes(const Field *field) + :length(field->character_octet_length() / field->charset()->mbmaxlen), + unireg_check(field->unireg_check), + interval(NULL), + charset(field->charset()), // May be NULL ptr + srid(0), + geom_type(Field::GEOM_GEOMETRY), + pack_flag(0) +{} + + /** Create a field suitable for create of table. */ Column_definition::Column_definition(THD *thd, Field *old_field, Field *orig_field) + :Column_definition_attributes(old_field) { on_update= NULL; field_name= old_field->field_name; - length= old_field->field_length; flags= old_field->flags; - unireg_check=old_field->unireg_check; pack_length=old_field->pack_length(); key_length= old_field->key_length(); set_handler(old_field->type_handler()); - charset= old_field->charset(); // May be NULL ptr comment= old_field->comment; decimals= old_field->decimals(); vcol_info= old_field->vcol_info; option_list= old_field->option_list; - pack_flag= 0; compression_method_ptr= 0; versioning= VERSIONING_NOT_SET; invisible= old_field->invisible; + interval_list.empty(); // prepare_interval_field() needs this + char_length= (uint) length; if (orig_field) { @@ -10903,66 +10552,9 @@ Column_definition::Column_definition(THD *thd, Field *old_field, check_constraint= 0; } - switch (real_field_type()) { - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - length/= charset->mbmaxlen; - key_length/= charset->mbmaxlen; - break; - case MYSQL_TYPE_STRING: - /* Change CHAR -> VARCHAR if dynamic record length */ - if (old_field->type() == MYSQL_TYPE_VAR_STRING) - set_handler(&type_handler_varchar); - /* fall through */ - - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_VAR_STRING: - /* This is corrected in create_length_to_internal_length */ - length= (length+charset->mbmaxlen-1) / charset->mbmaxlen - - MY_TEST(old_field->compression_method()); - break; -#ifdef HAVE_SPATIAL - case MYSQL_TYPE_GEOMETRY: - geom_type= ((Field_geom*)old_field)->geom_type; - srid= ((Field_geom*)old_field)->srid; - break; -#endif - case MYSQL_TYPE_YEAR: - if (length != 4) - { - char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1]; - my_snprintf(buff, sizeof(buff), "YEAR(%llu)", length); - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_WARN_DEPRECATED_SYNTAX, - ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX), - buff, "YEAR(4)"); - } - break; - case MYSQL_TYPE_FLOAT: - case MYSQL_TYPE_DOUBLE: - /* - Floating points are stored with FLOATING_POINT_DECIMALS but internally - in MariaDB used with NOT_FIXED_DEC, which is >= FLOATING_POINT_DECIMALS. - */ - if (decimals >= FLOATING_POINT_DECIMALS) - decimals= NOT_FIXED_DEC; - break; - default: - break; - } - - if (flags & (ENUM_FLAG | SET_FLAG)) - interval= ((Field_enum*) old_field)->typelib; - else - interval=0; - - interval_list.empty(); // prepare_interval_field() needs this + type_handler()->Column_definition_reuse_fix_attributes(thd, this, old_field); - char_length= (uint)length; + type_handler()->Column_definition_implicit_upgrade(this); /* Copy the default (constant/function) from the column object orig_field, if @@ -11044,11 +10636,11 @@ Column_definition::redefine_stage1_common(const Column_definition *dup_field, uint32 Field_blob::char_length() const { - return Field_blob::octet_length(); + return Field_blob::character_octet_length(); } -uint32 Field_blob::octet_length() const +uint32 Field_blob::character_octet_length() const { switch (packlength) { diff --git a/sql/field.h b/sql/field.h index 2037802df9a..d5b2a621d48 100644 --- a/sql/field.h +++ b/sql/field.h @@ -48,6 +48,9 @@ class Item_equal; class Virtual_tmp_table; class Qualified_column_ident; class Table_ident; +class SEL_ARG; +class RANGE_OPT_PARAM; +struct KEY_PART; enum enum_check_fields { @@ -467,31 +470,6 @@ inline bool is_temporal_type_with_date(enum_field_types type) } -/** - Convert temporal real types as retuned by field->real_type() - to field type as returned by field->type(). - - @param real_type Real type. - @retval Field type. -*/ -inline enum_field_types real_type_to_type(enum_field_types real_type) -{ - switch (real_type) - { - case MYSQL_TYPE_TIME2: - return MYSQL_TYPE_TIME; - case MYSQL_TYPE_DATETIME2: - return MYSQL_TYPE_DATETIME; - case MYSQL_TYPE_TIMESTAMP2: - return MYSQL_TYPE_TIMESTAMP; - case MYSQL_TYPE_NEWDATE: - return MYSQL_TYPE_DATE; - /* Note: NEWDECIMAL is a type, not only a real_type */ - default: return real_type; - } -} - - enum enum_vcol_info_type { VCOL_GENERATED_VIRTUAL, VCOL_GENERATED_STORED, @@ -835,7 +813,7 @@ public: return nr < 0 ? 0 : (ulonglong) nr; } virtual bool val_bool(void)= 0; - virtual my_decimal *val_decimal(my_decimal *); + virtual my_decimal *val_decimal(my_decimal *)=0; inline String *val_str(String *str) { return val_str(str, str); } /* val_str(buf1, buf2) gets two buffers and should use them as follows: @@ -872,6 +850,10 @@ public: to be quoted when used in constructing an SQL query. */ virtual bool str_needs_quotes() { return FALSE; } + const Type_handler *type_handler_for_comparison() const + { + return type_handler()->type_handler_for_comparison(); + } Item_result result_type () const { return type_handler()->result_type(); @@ -880,7 +862,6 @@ public: { return type_handler()->cmp_type(); } - static enum_field_types field_type_merge(enum_field_types, enum_field_types); virtual bool eq(Field *field) { return (ptr == field->ptr && null_ptr == field->null_ptr && @@ -1245,6 +1226,12 @@ public: virtual Field *new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uint32 length, uchar *new_null_ptr, uint new_null_bit); + Field *create_tmp_field(MEM_ROOT *root, TABLE *new_table, + bool maybe_null_arg); + Field *create_tmp_field(MEM_ROOT *root, TABLE *new_table) + { + return create_tmp_field(root, new_table, maybe_null()); + } Field *clone(MEM_ROOT *mem_root, TABLE *new_table); Field *clone(MEM_ROOT *mem_root, TABLE *new_table, my_ptrdiff_t diff, bool stat_flag= FALSE); @@ -1355,7 +1342,7 @@ public: } void copy_from_tmp(int offset); uint fill_cache_field(struct st_cache_field *copy); - virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + virtual bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); } virtual TYPELIB *get_typelib() const { return NULL; } virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } @@ -1395,6 +1382,59 @@ protected: } int warn_if_overflow(int op_result); Copy_func *get_identical_copy_func() const; + bool can_optimize_scalar_range(const RANGE_OPT_PARAM *param, + const KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, + const Item *value) const; + uchar *make_key_image(MEM_ROOT *mem_root, const KEY_PART *key_part); + SEL_ARG *get_mm_leaf_int(RANGE_OPT_PARAM *param, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value, + bool unsigned_field); + /* + Make a leaf tree for the cases when the value was stored + to the field exactly, without any truncation, rounding or adjustments. + For example, if we stored an INT value into an INT column, + and value->save_in_field_no_warnings() returned 0, + we know that the value was stored exactly. + */ + SEL_ARG *stored_field_make_mm_leaf_exact(RANGE_OPT_PARAM *param, + KEY_PART *key_part, + scalar_comparison_op op, + Item *value); + /* + Make a leaf tree for the cases when we don't know if + the value was stored to the field without any data loss, + or was modified to a smaller or a greater value. + Used for the data types whose methods Field::store*() + silently adjust the value. This is the most typical case. + */ + SEL_ARG *stored_field_make_mm_leaf(RANGE_OPT_PARAM *param, + KEY_PART *key_part, + scalar_comparison_op op, Item *value); + /* + Make a leaf tree when an INT value was stored into a field of INT type, + and some truncation happened. Tries to adjust the range search condition + when possible, e.g. "tinytint < 300" -> "tinyint <= 127". + Can also return SEL_ARG_IMPOSSIBLE(), and NULL (not sargable). + */ + SEL_ARG *stored_field_make_mm_leaf_bounded_int(RANGE_OPT_PARAM *param, + KEY_PART *key_part, + scalar_comparison_op op, + Item *value, + bool unsigned_field); + /* + Make a leaf tree when some truncation happened during + value->save_in_field_no_warning(this), and we cannot yet adjust the range + search condition for the current combination of the field and the value + data types. + Returns SEL_ARG_IMPOSSIBLE() for "=" and "<=>". + Returns NULL (not sargable) for other comparison operations. + */ + SEL_ARG *stored_field_make_mm_leaf_truncated(RANGE_OPT_PARAM *prm, + scalar_comparison_op, + Item *value); public: void set_table_name(String *alias) { @@ -1405,6 +1445,19 @@ public: orig_table= table= table_arg; set_table_name(&table_arg->alias); } + virtual void init_for_tmp_table(Field *org_field, TABLE *new_table) + { + init(new_table); + orig_table= org_field->orig_table; + vcol_info= 0; + cond_selectivity= 1.0; + next_equal_field= NULL; + option_list= NULL; + option_struct= NULL; + if (org_field->type() == MYSQL_TYPE_VAR_STRING || + org_field->type() == MYSQL_TYPE_VARCHAR) + new_table->s->db_create_options|= HA_OPTION_PACK_RECORD; + } void init_for_make_new_field(TABLE *new_table_arg, TABLE *orig_table_arg) { init(new_table_arg); @@ -1431,12 +1484,21 @@ public: /* convert decimal to longlong with overflow check */ longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag, int *err); + /* + Maximum number of bytes in character representation. + - For string types it is equal to the field capacity, in bytes. + - For non-string types it represents the longest possible string length + after conversion to string. + */ + virtual uint32 character_octet_length() const + { + return field_length; + } /* The max. number of characters */ virtual uint32 char_length() const { return field_length / charset()->mbmaxlen; } - virtual geometry_type get_geometry_type() { /* shouldn't get here. */ @@ -1554,6 +1616,10 @@ public: const Item *item, bool is_eq_func) const; + virtual SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value)= 0; + bool can_optimize_outer_join_table_elimination(const Item_bool_func *cond, const Item *item) const { @@ -1715,6 +1781,9 @@ public: { return pos_in_interval_val_real(min, max); } + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value); }; @@ -1752,6 +1821,7 @@ public: enum Derivation derivation(void) const { return field_derivation; } bool binary() const { return field_charset == &my_charset_bin; } uint32 max_display_length() const { return field_length; } + uint32 character_octet_length() const { return field_length; } uint32 char_length() const { return field_length / field_charset->mbmaxlen; } Information_schema_character_attributes information_schema_character_attributes() const @@ -1771,6 +1841,9 @@ public: return pos_in_interval_val_str(min, max, length_size()); } bool test_if_equality_guarantees_uniqueness(const Item *const_item) const; + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value); }; /* base class for Field_string, Field_varstring and Field_blob */ @@ -1883,9 +1956,9 @@ public: return Field_num::memcpy_field_possible(from) && field_length >= from->field_length; } - int store_decimal(const my_decimal *); + int store_decimal(const my_decimal *dec) { return store(dec->to_double()); } int store_time_dec(const MYSQL_TIME *ltime, uint dec); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); my_decimal *val_decimal(my_decimal *); bool val_bool() { return val_real() != 0e0; } uint32 max_display_length() const { return field_length; } @@ -1966,8 +2039,8 @@ public: } int save_in_field(Field *to) { - my_decimal buff; - return to->store_decimal(val_decimal(&buff)); + my_decimal tmp(ptr, precision, dec); + return to->store_decimal(&tmp); } bool memcpy_field_possible(const Field *from) const { @@ -1983,17 +2056,33 @@ public: int store(longlong nr, bool unsigned_val); int store_time_dec(const MYSQL_TIME *ltime, uint dec); int store_decimal(const my_decimal *); - double val_real(void); - longlong val_int(void); - ulonglong val_uint(void); + double val_real(void) + { + return my_decimal(ptr, precision, dec).to_double(); + } + longlong val_int(void) + { + return my_decimal(ptr, precision, dec).to_longlong(unsigned_flag); + } + ulonglong val_uint(void) + { + return (ulonglong) my_decimal(ptr, precision, dec).to_longlong(true); + } my_decimal *val_decimal(my_decimal *); - String *val_str(String*, String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + String *val_str(String *val_buffer, String *val_ptr __attribute__((unused))) + { + uint fixed_precision= zerofill ? precision : 0; + return my_decimal(ptr, precision, dec). + to_string(val_buffer, fixed_precision, dec, '0'); + } + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) + { + return my_decimal(ptr, precision, dec). + to_datetime_with_warn(get_thd(), ltime, fuzzydate, field_name.str); + } bool val_bool() { - my_decimal decimal_value; - my_decimal *val= val_decimal(&decimal_value); - return val ? !my_decimal_is_zero(val) : 0; + return my_decimal(ptr, precision, dec).to_bool(); } int cmp(const uchar *, const uchar *); void sort_string(uchar *buff, uint length); @@ -2038,7 +2127,7 @@ public: return nr < 0 && !unsigned_flag ? 0 : (ulonglong) nr; } int store_time_dec(const MYSQL_TIME *ltime, uint dec); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); virtual const Type_limits_int *type_limits_int() const= 0; uint32 max_display_length() const { @@ -2068,6 +2157,12 @@ public: uint32 prec= type_limits_int()->precision(); return Information_schema_numeric_attributes(prec, 0); } + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value) + { + return get_mm_leaf_int(param, key_part, cond, op, value, unsigned_flag); + } }; @@ -2315,8 +2410,8 @@ public: {} const Type_handler *type_handler() const { return &type_handler_vers_trx_id; } uint size_of() const { return sizeof(*this); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return get_date(ltime, fuzzydate, (ulonglong) val_int()); } @@ -2416,6 +2511,11 @@ public: if (dec_arg >= FLOATING_POINT_DECIMALS) dec_arg= NOT_FIXED_DEC; } + void init_for_tmp_table(Field *org_field, TABLE *new_table) + { + Field::init_for_tmp_table(org_field, new_table); + not_fixed= true; + } const Type_handler *type_handler() const { return &type_handler_double; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; } int store(const char *to,size_t length,CHARSET_INFO *charset); @@ -2494,6 +2594,35 @@ class Field_temporal: public Field { protected: Item *get_equal_const_item_datetime(THD *thd, const Context &ctx, Item *const_item); + void set_warnings(Sql_condition::enum_warning_level trunc_level, + const ErrConv *str, int was_cut, timestamp_type ts_type); + int store_TIME_return_code_with_warnings(int warn, const ErrConv *str, + timestamp_type ts_type) + { + if (!MYSQL_TIME_WARN_HAVE_WARNINGS(warn) && + MYSQL_TIME_WARN_HAVE_NOTES(warn)) + { + set_warnings(Sql_condition::WARN_LEVEL_NOTE, str, + warn | MYSQL_TIME_WARN_TRUNCATED, ts_type); + return 3; + } + set_warnings(Sql_condition::WARN_LEVEL_WARN, str, warn, ts_type); + return warn ? 2 : 0; + } + int store_invalid_with_warning(const ErrConv *str, int was_cut, + timestamp_type ts_type) + { + reset(); + Sql_condition::enum_warning_level level= Sql_condition::WARN_LEVEL_WARN; + if (was_cut == 0) // special case: zero date + { + DBUG_ASSERT(ts_type != MYSQL_TIMESTAMP_TIME); + set_warnings(level, str, MYSQL_TIME_WARN_OUT_OF_RANGE, ts_type); + return 2; + } + set_warnings(level, str, MYSQL_TIME_WARN_TRUNCATED, ts_type); + return 1; + } public: Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -2509,7 +2638,7 @@ public: int save_in_field(Field *to) { MYSQL_TIME ltime; - if (get_date(<ime, 0)) + if (get_date(<ime, date_mode_t(0))) return to->reset(); return to->store_time_dec(<ime, decimals()); } @@ -2528,8 +2657,6 @@ public: return (Field::eq_def(field) && decimals() == field->decimals()); } my_decimal *val_decimal(my_decimal*); - void set_warnings(Sql_condition::enum_warning_level trunc_level, - const ErrConv *str, int was_cut, timestamp_type ts_type); double pos_in_interval(Field *min, Field *max) { return pos_in_interval_val_real(min, max); @@ -2544,6 +2671,9 @@ public: { return true; } + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value); }; @@ -2556,18 +2686,16 @@ public: */ class Field_temporal_with_date: public Field_temporal { protected: - int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, - int was_cut, int have_smth_to_conv); - virtual void store_TIME(MYSQL_TIME *ltime) = 0; + virtual void store_TIME(const MYSQL_TIME *ltime) = 0; virtual bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const = 0; + date_mode_t fuzzydate) const = 0; bool validate_MMDD(bool not_zero_date, uint month, uint day, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { if (!not_zero_date) - return fuzzydate & TIME_NO_ZERO_DATE; + return bool(fuzzydate & TIME_NO_ZERO_DATE); if (!month || !day) - return fuzzydate & TIME_NO_ZERO_IN_DATE; + return bool(fuzzydate & TIME_NO_ZERO_IN_DATE); return false; } public: @@ -2578,20 +2706,19 @@ public: :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) {} - int store(const char *to, size_t length, CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_time_dec(const MYSQL_TIME *ltime, uint dec); - int store_decimal(const my_decimal *); bool validate_value_in_record(THD *thd, const uchar *record) const; }; class Field_timestamp :public Field_temporal { protected: - sql_mode_t sql_mode_for_timestamp(THD *thd) const; - int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *, - int warnings, bool have_smth_to_conv); + date_mode_t sql_mode_for_timestamp(THD *thd) const; + int store_TIME_with_warning(THD *, const Datetime *, + const ErrConv *, int warn); + virtual void store_TIMEVAL(const timeval &tv) + { + int4store(ptr, tv.tv_sec); + } public: Field_timestamp(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, @@ -2631,11 +2758,11 @@ public: { return get_timestamp(ptr, sec_part); } - virtual void store_TIME(my_time_t timestamp, ulong sec_part) + void store_TIME(my_time_t timestamp, ulong sec_part) { - int4store(ptr,timestamp); + store_TIMEVAL(Timeval(timestamp, sec_part).trunc(decimals())); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); uchar *pack(uchar *to, const uchar *from, uint max_length __attribute__((unused))) { @@ -2703,6 +2830,7 @@ class Field_timestamp_hires :public Field_timestamp_with_dec { { return Type_handler_timestamp::sec_part_bytes(dec); } + void store_TIMEVAL(const timeval &tv); public: Field_timestamp_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, @@ -2715,7 +2843,6 @@ public: DBUG_ASSERT(dec); } my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; - void store_TIME(my_time_t timestamp, ulong sec_part); int cmp(const uchar *,const uchar *); uint32 pack_length() const { return 4 + sec_part_bytes(dec); } uint size_of() const { return sizeof(*this); } @@ -2731,6 +2858,7 @@ class Field_timestampf :public Field_timestamp_with_dec { *metadata_ptr= (uchar) decimals(); return 1; } + void store_TIMEVAL(const timeval &tv); public: Field_timestampf(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, @@ -2759,7 +2887,6 @@ public: } void set_max(); bool is_max(); - void store_TIME(my_time_t timestamp, ulong sec_part); my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; my_time_t get_timestamp(ulong *sec_part) const { @@ -2777,7 +2904,10 @@ public: :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 1, 1) {} - const Type_handler *type_handler() const { return &type_handler_year; } + const Type_handler *type_handler() const + { + return field_length == 2 ? &type_handler_year2 : &type_handler_year; + } Copy_func *get_copy_func(const Field *from) const { if (eq_def(from)) @@ -2812,7 +2942,7 @@ public: double val_real(void); longlong val_int(void); String *val_str(String*,String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); bool send_binary(Protocol *protocol); Information_schema_numeric_attributes information_schema_numeric_attributes() const @@ -2824,18 +2954,43 @@ public: }; -class Field_date :public Field_temporal_with_date { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; +class Field_date_common: public Field_temporal_with_date +{ +protected: + int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str, + int was_cut); +public: + Field_date_common(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg) + :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, + null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg) + {} + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value); + int store(const char *to, size_t length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_time_dec(const MYSQL_TIME *ltime, uint dec); + int store_decimal(const my_decimal *); +}; + + +class Field_date :public Field_date_common +{ + void store_TIME(const MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; public: Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg) - :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg) {} + :Field_date_common(ptr_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg) {} const Type_handler *type_handler() const { return &type_handler_date; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_date::get_TIME(ltime, ptr, fuzzydate); } double val_real(void); longlong val_int(void); @@ -2859,14 +3014,15 @@ public: }; -class Field_newdate :public Field_temporal_with_date { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; +class Field_newdate :public Field_date_common +{ + void store_TIME(const MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; public: Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg) - :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg) + :Field_date_common(ptr_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg) {} const Type_handler *type_handler() const { return &type_handler_newdate; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; } @@ -2879,7 +3035,7 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 3; } void sql_type(String &str) const; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_newdate::get_TIME(ltime, ptr, fuzzydate); } uint size_of() const { return sizeof(*this); } Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); @@ -2895,14 +3051,8 @@ class Field_time :public Field_temporal { long curdays; protected: virtual void store_TIME(const MYSQL_TIME *ltime); - int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, - int was_cut, int have_smth_to_conv); - void set_warnings(Sql_condition::enum_warning_level level, - const ErrConv *str, int was_cut) - { - Field_temporal::set_warnings(level, str, was_cut, MYSQL_TIMESTAMP_TIME); - } - bool check_zero_in_date_with_warn(ulonglong fuzzydate); + int store_TIME_with_warning(const Time *ltime, const ErrConv *str, int warn); + bool check_zero_in_date_with_warn(date_mode_t fuzzydate); static void do_field_time(Copy_field *copy); public: Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, @@ -2936,7 +3086,7 @@ public: double val_real(void); longlong val_int(void); String *val_str(String*,String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); bool send_binary(Protocol *protocol); int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); @@ -2997,7 +3147,7 @@ public: ((TIME_MAX_VALUE_SECONDS+1LL)*TIME_SECOND_PART_FACTOR), dec); } int reset(void); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); uint32 pack_length() const { return Type_handler_time::hires_bytes(dec); } @@ -3048,14 +3198,17 @@ public: return memcmp(a_ptr, b_ptr, pack_length()); } int reset(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); uint size_of() const { return sizeof(*this); } }; class Field_datetime :public Field_temporal_with_date { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; + void store_TIME(const MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; +protected: + int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str, + int was_cut); public: Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, @@ -3069,6 +3222,11 @@ public: } const Type_handler *type_handler() const { return &type_handler_datetime; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } + int store(const char *to, size_t length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_time_dec(const MYSQL_TIME *ltime, uint dec); + int store_decimal(const my_decimal *); double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -3077,7 +3235,7 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 8; } void sql_type(String &str) const; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_datetime::get_TIME(ltime, ptr, fuzzydate); } int set_time(); int evaluate_update_default_function() @@ -3147,8 +3305,8 @@ public: DATETIME(1..6) */ class Field_datetime_hires :public Field_datetime_with_dec { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; + void store_TIME(const MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; public: Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, @@ -3160,7 +3318,7 @@ public: } int cmp(const uchar *,const uchar *); uint32 pack_length() const { return Type_handler_datetime::hires_bytes(dec); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_datetime_hires::get_TIME(ltime, ptr, fuzzydate); } uint size_of() const { return sizeof(*this); } }; @@ -3170,8 +3328,8 @@ public: DATETIME(0..6) - MySQL56 version */ class Field_datetimef :public Field_datetime_with_dec { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; + void store_TIME(const MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; int save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= (uchar) decimals(); @@ -3202,7 +3360,7 @@ public: return memcmp(a_ptr, b_ptr, pack_length()); } int reset(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_datetimef::get_TIME(ltime, ptr, fuzzydate); } uint size_of() const { return sizeof(*this); } }; @@ -3475,6 +3633,7 @@ private: str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/")); } uint32 max_display_length() const { return field_length - 1; } + uint32 character_octet_length() const { return field_length - 1; } uint32 char_length() const { return (field_length - 1) / field_charset->mbmaxlen; @@ -3607,7 +3766,7 @@ public: Information_schema_character_attributes information_schema_character_attributes() const { - uint32 octets= Field_blob::octet_length(); + uint32 octets= Field_blob::character_octet_length(); uint32 chars= octets / field_charset->mbminlen; return Information_schema_character_attributes(octets, chars); } @@ -3773,7 +3932,7 @@ public: { return charset() == &my_charset_bin ? FALSE : TRUE; } uint32 max_display_length() const; uint32 char_length() const; - uint32 octet_length() const; + uint32 character_octet_length() const; uint is_equal(Create_field *new_field); friend void TABLE::remember_blob_values(String *blob_storage); @@ -4181,6 +4340,12 @@ public: } void hash(ulong *nr, ulong *nr2); + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value) + { + return get_mm_leaf_int(param, key_part, cond, op, value, true); + } private: virtual size_t do_last_null_byte() const; int save_field_metadata(uchar *first_byte); @@ -4225,21 +4390,53 @@ public: extern const LEX_CSTRING null_clex_str; -Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, - uchar *ptr, uint32 field_length, - uchar *null_pos, uchar null_bit, - uint pack_flag, const Type_handler *handler, - CHARSET_INFO *cs, - Field::geometry_type geom_type, uint srid, - Field::utype unireg_check, - TYPELIB *interval, const LEX_CSTRING *field_name, - uint32 flags); +class Column_definition_attributes +{ +public: + /* + At various stages in execution this can be length of field in bytes or + max number of characters. + */ + ulonglong length; + Field::utype unireg_check; + TYPELIB *interval; // Which interval to use + CHARSET_INFO *charset; + uint32 srid; + Field::geometry_type geom_type; + uint pack_flag; + Column_definition_attributes() + :length(0), + unireg_check(Field::NONE), + interval(NULL), + charset(&my_charset_bin), + srid(0), + geom_type(Field::GEOM_GEOMETRY), + pack_flag(0) + { } + Column_definition_attributes(const Field *field); + Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, + const Record_addr *rec, + const Type_handler *handler, + const LEX_CSTRING *field_name, + uint32 flags) const; + uint temporal_dec(uint intlen) const + { + return (uint) (length > intlen ? length - intlen - 1 : 0); + } + uint pack_flag_to_pack_length() const; + void frm_pack_basic(uchar *buff) const; + void frm_pack_charset(uchar *buff) const; + void frm_unpack_basic(const uchar *buff); + bool frm_unpack_charset(TABLE_SHARE *share, const uchar *buff); +}; + /* Create field class for CREATE TABLE */ class Column_definition: public Sql_alloc, - public Type_handler_hybrid_field_type + public Type_handler_hybrid_field_type, + public Column_definition_attributes { /** Create "interval" from "interval_list". @@ -4294,11 +4491,6 @@ public: WITHOUT_VERSIONING }; Item *on_update; // ON UPDATE NOW() - /* - At various stages in execution this can be length of field in bytes or - max number of characters. - */ - ulonglong length; field_visibility_t invisible; /* The value of `length' as set by parser: is the number of characters @@ -4306,15 +4498,9 @@ public: */ uint32 char_length; uint decimals, flags, pack_length, key_length; - Field::utype unireg_check; - TYPELIB *interval; // Which interval to use List<String> interval_list; - CHARSET_INFO *charset; - uint32 srid; - Field::geometry_type geom_type; engine_option_value *option_list; - uint pack_flag; /* This is additinal data provided for any computed(virtual) field. @@ -4332,11 +4518,9 @@ public: :Type_handler_hybrid_field_type(&type_handler_null), compression_method_ptr(0), comment(null_clex_str), - on_update(NULL), length(0), invisible(VISIBLE), decimals(0), - flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE), - interval(0), charset(&my_charset_bin), - srid(0), geom_type(Field::GEOM_GEOMETRY), - option_list(NULL), pack_flag(0), + on_update(NULL), invisible(VISIBLE), decimals(0), + flags(0), pack_length(0), key_length(0), + option_list(NULL), vcol_info(0), default_value(0), check_constraint(0), versioning(VERSIONING_NOT_SET) { @@ -4466,20 +4650,18 @@ public: } Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, - uchar *ptr, uchar *null_pos, uchar null_bit, + const Record_addr *addr, const LEX_CSTRING *field_name_arg) const { - return ::make_field(share, mem_root, ptr, - (uint32)length, null_pos, null_bit, - pack_flag, type_handler(), charset, - geom_type, srid, unireg_check, interval, - field_name_arg, flags); + return Column_definition_attributes::make_field(share, mem_root, addr, + type_handler(), + field_name_arg, flags); } Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *field_name_arg) const { - return make_field(share, mem_root, (uchar *) 0, (uchar *) "", 0, - field_name_arg); + Record_addr addr(true); + return make_field(share, mem_root, &addr, field_name_arg); } /* Return true if default is an expression that must be saved explicitely */ bool has_default_expression(); @@ -4801,7 +4983,7 @@ bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name, #define FIELDFLAG_DEC_SHIFT 8 #define FIELDFLAG_MAX_DEC 63U -#define MTYP_TYPENR(type) (type & 127U) /* Remove bits from type */ +#define MTYP_TYPENR(type) ((type) & 127U) // Remove bits from type #define f_is_dec(x) ((x) & FIELDFLAG_DECIMAL) #define f_is_num(x) ((x) & FIELDFLAG_NUMBER) diff --git a/sql/field_conv.cc b/sql/field_conv.cc index dddb2182051..8b3d9c04656 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -413,8 +413,8 @@ void Field::do_field_real(Copy_field *copy) void Field::do_field_decimal(Copy_field *copy) { - my_decimal value; - copy->to_field->store_decimal(copy->from_field->val_decimal(&value)); + my_decimal value(copy->from_field); + copy->to_field->store_decimal(&value); } @@ -429,7 +429,7 @@ void Field::do_field_temporal(Copy_field *copy) { MYSQL_TIME ltime; // TODO: we now need to check result - if (copy->from_field->get_date(<ime, 0)) + if (copy->from_field->get_date(<ime, date_mode_t(0))) copy->to_field->reset(); else copy->to_field->store_time_dec(<ime, copy->from_field->decimals()); diff --git a/sql/filesort.cc b/sql/filesort.cc index a4be9e4acfa..6f2a6096aa2 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -258,7 +258,7 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort, } if (memory_available < min_sort_memory) { - my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR)); + my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR_LOG + ME_FATAL)); goto err; } tracker->report_sort_buffer_size(sort->sort_buffer_size()); @@ -710,7 +710,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, uchar *ref_pos, *next_pos, ref_buff[MAX_REFLENGTH]; TABLE *sort_form; handler *file; - MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set; + MY_BITMAP *save_read_set, *save_write_set; Item *sort_cond; ha_rows retval; DBUG_ENTER("find_all_keys"); @@ -745,13 +745,11 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, /* Remember original bitmaps */ save_read_set= sort_form->read_set; save_write_set= sort_form->write_set; - save_vcol_set= sort_form->vcol_set; /* Set up temporary column read map for columns used by sort */ DBUG_ASSERT(save_read_set != &sort_form->tmp_set); bitmap_clear_all(&sort_form->tmp_set); - sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set, - &sort_form->tmp_set); + sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set); register_used_fields(param); if (quick_select) select->quick->add_used_key_part_to_set(); @@ -809,16 +807,12 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, */ MY_BITMAP *tmp_read_set= sort_form->read_set; MY_BITMAP *tmp_write_set= sort_form->write_set; - MY_BITMAP *tmp_vcol_set= sort_form->vcol_set; if (select->cond->with_subquery()) - sort_form->column_bitmaps_set(save_read_set, save_write_set, - save_vcol_set); + sort_form->column_bitmaps_set(save_read_set, save_write_set); write_record= (select->skip_record(thd) > 0); if (select->cond->with_subquery()) - sort_form->column_bitmaps_set(tmp_read_set, - tmp_write_set, - tmp_vcol_set); + sort_form->column_bitmaps_set(tmp_read_set, tmp_write_set); } else write_record= true; @@ -864,7 +858,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, } /* Signal we should use orignal column read and write maps */ - sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set); + sort_form->column_bitmaps_set(save_read_set, save_write_set); if (unlikely(thd->is_error())) DBUG_RETURN(HA_POS_ERROR); @@ -872,8 +866,8 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos)); if (unlikely(error != HA_ERR_END_OF_FILE)) { - file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); // purecov: inspected - DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ + file->print_error(error,MYF(ME_ERROR_LOG)); + DBUG_RETURN(HA_POS_ERROR); } if (indexpos && idx && write_keys(param, fs_info, idx, buffpek_pointers, tempfile)) @@ -885,7 +879,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, DBUG_RETURN(retval); err: - sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set); + sort_form->column_bitmaps_set(save_read_set, save_write_set); DBUG_RETURN(HA_POS_ERROR); } /* find_all_keys */ @@ -1063,7 +1057,7 @@ Type_handler_temporal_result::make_sort_key(uchar *to, Item *item, Sort_param *param) const { MYSQL_TIME buf; - if (item->get_date_result(&buf, TIME_INVALID_DATES)) + if (item->get_date_result(current_thd, &buf, TIME_INVALID_DATES)) { DBUG_ASSERT(item->maybe_null); DBUG_ASSERT(item->null_value); @@ -1122,9 +1116,8 @@ Type_handler_decimal_result::make_sort_key(uchar *to, Item *item, } *to++= 1; } - my_decimal2binary(E_DEC_FATAL_ERROR, dec_val, to, - item->max_length - (item->decimals ? 1 : 0), - item->decimals); + dec_val->to_binary(to, item->max_length - (item->decimals ? 1 : 0), + item->decimals); } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index c985ada64e6..37a3decdbca 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -6814,7 +6814,7 @@ FT_INFO *ha_partition::ft_init_ext(uint flags, uint inx, String *key) sizeof(FT_INFO *) * m_tot_parts, NullS))) { - my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); + my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL)); DBUG_RETURN(NULL); } ft_target->part_ft_info= tmp_ft_info; @@ -10688,8 +10688,6 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair) { /* Only need to read the partitioning fields. */ bitmap_union(table->read_set, &m_part_info->full_part_field_set); - if (table->vcol_set) - bitmap_union(table->vcol_set, &m_part_info->full_part_field_set); } if ((result= m_file[read_part_id]->ha_rnd_init(1))) diff --git a/sql/handle_connections_win.cc b/sql/handle_connections_win.cc new file mode 100644 index 00000000000..b37b4dedad1 --- /dev/null +++ b/sql/handle_connections_win.cc @@ -0,0 +1,555 @@ +/* Copyright (c) 2018 MariaDB Corporation. + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +/* Accepting connections on Windows */ + +#include <my_global.h> +#include <sql_class.h> +#include <sql_connect.h> +#include <mysqld.h> +#include <mswsock.h> +#include <mysql/psi/mysql_socket.h> +#include <sddl.h> + +#include <handle_connections_win.h> + +/* From mysqld.cc */ +extern HANDLE hEventShutdown; +extern MYSQL_SOCKET base_ip_sock, extra_ip_sock; +extern PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ(); +extern void tp_win_callback_prolog(); +static SECURITY_ATTRIBUTES pipe_security; + +/** + Abstract base class for accepting new connection, + asynchronously (i.e the accept() operation can be posted, + and result is retrieved later) , and creating a new connection. +*/ + +struct Listener +{ + /** Windows handle of the Listener. + Subclasses would use SOCKET or named pipe handle + */ + HANDLE m_handle; + /** Required for all async IO*/ + OVERLAPPED m_overlapped; + + /** Create new listener + @param handle - @see m_handle + @param wait_handle - usually, event handle or INVALID_HANDLE_VALUE + @see wait_handle + */ + Listener(HANDLE handle, HANDLE wait_handle): + m_handle(handle), m_overlapped() + { + m_overlapped.hEvent= wait_handle; + } + + /** + if not NULL, this handle can be be used in WaitForSingle/MultipleObject(s). + This handle will be closed when object is destroyed. + + If NULL, the completion notification happens in threadpool. + */ + HANDLE wait_handle() + { + return m_overlapped.hEvent; + } + + /* Start waiting for new client connection. */ + virtual void begin_accept()= 0; + + /** + Completion callback,called whenever IO posted by begin_accept is finisjed + Listener needs to create a new THD then (or, call scheduler so it creates one) + + @param success - whether IO completed successfull + */ + virtual void completion_callback(bool success)= 0; + + /** + Completion callback for Listener, that uses events for waiting + to IO. Not suitable for threadpool etc. Retrieves the status of + completed IO from the OVERLAPPED structure + */ + void completion_callback() + { + DBUG_ASSERT(wait_handle() && (wait_handle() != INVALID_HANDLE_VALUE)); + DWORD bytes; + return completion_callback( + GetOverlappedResult(wait_handle(), &m_overlapped, &bytes, FALSE)); + } + + /** Cancel an in-progress IO. Useful for threadpool-bound IO */ + void cancel() + { + CancelIoEx(m_handle, &m_overlapped); + } + + /* Destructor. Closes wait handle, if it was passed in constructor */ + virtual ~Listener() + { + if (m_overlapped.hEvent) + CloseHandle(m_overlapped.hEvent); + }; +}; + +/* Winsock extension finctions. */ +static LPFN_ACCEPTEX my_AcceptEx; +static LPFN_GETACCEPTEXSOCKADDRS my_GetAcceptExSockaddrs; + +/** + Listener that handles socket connections. + Can be threadpool-bound (i.e the completion is executed in threadpool thread), + or use events for waits. + + Threadpool-bound listener should be used with theradpool scheduler, for better + performance. +*/ +struct Socket_Listener: public Listener +{ + /** Client socket passed to AcceptEx() call.*/ + SOCKET m_client_socket; + + /** Buffer for sockaddrs passed to AcceptEx()/GetAcceptExSockaddrs() */ + char m_buffer[2 * sizeof(sockaddr_storage) + 32]; + + /* Threadpool IO struct.*/ + PTP_IO m_tp_io; + + /** + Callback for Windows threadpool's StartThreadpoolIo() function. + */ + static void CALLBACK tp_accept_completion_callback( + PTP_CALLBACK_INSTANCE, PVOID context, PVOID , ULONG io_result, + ULONG_PTR, PTP_IO io) + { + tp_win_callback_prolog(); + Listener *listener= (Listener *)context; + + if (io_result == ERROR_OPERATION_ABORTED) + { + /* ERROR_OPERATION_ABORTED caused by CancelIoEx()*/ + CloseThreadpoolIo(io); + delete listener; + return; + } + listener->completion_callback(io_result == 0); + } + + /** + Constructor + @param listen_socket - listening socket + @PTP_CALLBACK_ENVIRON callback_environ - threadpool environment, or NULL + if threadpool is not used for completion callbacks. + */ + Socket_Listener(MYSQL_SOCKET listen_socket, PTP_CALLBACK_ENVIRON callback_environ) : + Listener((HANDLE)listen_socket.fd,0), + m_client_socket(INVALID_SOCKET) + { + if (callback_environ) + { + /* Accept executed in threadpool. */ + m_tp_io= CreateThreadpoolIo(m_handle, + tp_accept_completion_callback, this, callback_environ); + } + else + { + /* Completion signaled via event. */ + m_tp_io= 0; + m_overlapped.hEvent= CreateEvent(0, FALSE , FALSE, 0); + } + } + + /* + Use AcceptEx to asynchronously wait for new connection; + */ + void begin_accept() + { +retry : + m_client_socket= socket(server_socket_ai_family, SOCK_STREAM, IPPROTO_TCP); + if (m_client_socket == INVALID_SOCKET) + { + sql_perror("socket() call failed."); + unireg_abort(1); + } + + DWORD bytes_received; + if (m_tp_io) + StartThreadpoolIo(m_tp_io); + + BOOL ret= my_AcceptEx( + (SOCKET)m_handle, + m_client_socket, + m_buffer, + 0, + sizeof(sockaddr_storage) + 16, + sizeof(sockaddr_storage) + 16, + &bytes_received, + &m_overlapped); + + DWORD last_error= ret? 0: WSAGetLastError(); + if (last_error == WSAECONNRESET) + { + if (m_tp_io) + CancelThreadpoolIo(m_tp_io); + goto retry; + } + + if (ret || last_error == ERROR_IO_PENDING || abort_loop) + return; + + sql_print_error("my_AcceptEx failed, last error %u", last_error); + abort(); + } + + /* Create new socket connection.*/ + void completion_callback(bool success) + { + if (!success) + { + /* my_AcceptEx() returned error */ + closesocket(m_client_socket); + begin_accept(); + return; + } + + MYSQL_SOCKET s_client{m_client_socket}; + MYSQL_SOCKET s_listen{(SOCKET)m_handle}; + +#ifdef HAVE_PSI_SOCKET_INTERFACE + /* Parse socket addresses buffer filled by AcceptEx(), + only needed for PSI instrumentation. */ + sockaddr *local_addr, *remote_addr; + int local_addr_len, remote_addr_len; + + my_GetAcceptExSockaddrs(m_buffer, + 0, sizeof(sockaddr_storage) + 16, sizeof(sockaddr_storage) + 16, + &local_addr, &local_addr_len, &remote_addr, &remote_addr_len); + + s_client.m_psi= PSI_SOCKET_CALL(init_socket) + (key_socket_client_connection, (const my_socket*)&s_listen.fd, remote_addr, remote_addr_len); +#endif + + /* Start accepting new connection. After this point, do not use + any member data, they could be used by a different (threadpool) thread. */ + begin_accept(); + + /* Some chores post-AcceptEx() that we need to create a normal socket.*/ + if (setsockopt(s_client.fd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + (char *)&s_listen.fd, sizeof(s_listen.fd))) + { + if (!abort_loop) + { + sql_perror("setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed."); + abort(); + } + } + + /* Create a new connection.*/ + handle_accepted_socket(s_client, s_listen); + } + + ~Socket_Listener() + { + if (m_client_socket != INVALID_SOCKET) + closesocket(m_client_socket); + } + + /* + Retrieve the pointer to the Winsock extension functions + AcceptEx and GetAcceptExSockaddrs. + */ + static void init_winsock_extensions() + { + SOCKET s= mysql_socket_getfd(base_ip_sock); + if (s == INVALID_SOCKET) + s= mysql_socket_getfd(extra_ip_sock); + if (s == INVALID_SOCKET) + { + /* --skip-networking was used*/ + return; + } + GUID guid_AcceptEx= WSAID_ACCEPTEX; + GUID guid_GetAcceptExSockaddrs= WSAID_GETACCEPTEXSOCKADDRS; + + GUID *guids[]= { &guid_AcceptEx, &guid_GetAcceptExSockaddrs }; + void *funcs[]= { &my_AcceptEx, &my_GetAcceptExSockaddrs }; + DWORD bytes; + for (int i= 0; i < array_elements(guids); i++) + { + if (WSAIoctl(s, + SIO_GET_EXTENSION_FUNCTION_POINTER, + guids[i], sizeof(GUID), + funcs[i], sizeof(void *), + &bytes, 0, 0) == -1) + { + sql_print_error("WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER) failed"); + unireg_abort(1); + } + } + } +}; + + +/** + Pipe Listener. + Only event notification mode is implemented, no threadpool +*/ +struct Pipe_Listener : public Listener +{ + PTP_CALLBACK_ENVIRON m_tp_env; + Pipe_Listener(): + Listener(INVALID_HANDLE_VALUE, CreateEvent(0, FALSE, FALSE, 0)), + m_tp_env(get_threadpool_win_callback_environ()) + { + } + + /* + Creates local named pipe instance \\.\pipe\$socket for named pipe connection. + */ + static HANDLE create_named_pipe() + { + static bool first_instance= true; + static char pipe_name[512]; + DWORD open_mode= PIPE_ACCESS_DUPLEX | + FILE_FLAG_OVERLAPPED; + + if (first_instance) + { + snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\%s", mysqld_unix_port); + open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE; + if (!ConvertStringSecurityDescriptorToSecurityDescriptorA( + "S:(ML;; NW;;; LW) D:(A;; FRFW;;; WD)", + 1, &pipe_security.lpSecurityDescriptor, NULL)) + { + sql_perror("Can't start server : Initialize security descriptor"); + unireg_abort(1); + } + pipe_security.nLength= sizeof(SECURITY_ATTRIBUTES); + pipe_security.bInheritHandle= FALSE; + } + HANDLE pipe_handle= CreateNamedPipe(pipe_name, + open_mode, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + (int)global_system_variables.net_buffer_length, + (int)global_system_variables.net_buffer_length, + NMPWAIT_USE_DEFAULT_WAIT, + &pipe_security); + if (pipe_handle == INVALID_HANDLE_VALUE) + { + sql_perror("Create named pipe failed"); + sql_print_error("Aborting\n"); + exit(1); + } + first_instance= false; + return pipe_handle; + } + + static void create_pipe_connection(HANDLE pipe) + { + CONNECT *connect; + if (!(connect= new CONNECT) || !(connect->vio= vio_new_win32pipe(pipe))) + { + CloseHandle(pipe); + delete connect; + statistic_increment(aborted_connects, &LOCK_status); + statistic_increment(connection_errors_internal, &LOCK_status); + return; + } + connect->host= my_localhost; + create_new_thread(connect); + } + + /* Threadpool callback.*/ + static void CALLBACK tp_create_pipe_connection( + PTP_CALLBACK_INSTANCE,void *Context) + { + tp_win_callback_prolog(); + create_pipe_connection(Context); + } + + void begin_accept() + { + m_handle= create_named_pipe(); + BOOL connected= ConnectNamedPipe(m_handle, &m_overlapped); + if (connected) + { + /* Overlapped ConnectNamedPipe should return zero. */ + sql_perror("Overlapped ConnectNamedPipe() already connected."); + abort(); + } + DWORD last_error= GetLastError(); + switch (last_error) + { + case ERROR_PIPE_CONNECTED: + /* Client is already connected, so signal an event.*/ + { + /* + Cleanup overlapped (so that subsequent GetOverlappedResult() + does not show results of previous IO + */ + HANDLE e= m_overlapped.hEvent; + memset(&m_overlapped, 0, sizeof(m_overlapped)); + m_overlapped.hEvent = e; + } + if (!SetEvent(m_overlapped.hEvent)) + { + sql_perror("SetEvent() failed for connected pipe."); + abort(); + } + break; + case ERROR_IO_PENDING: + break; + default: + sql_perror("ConnectNamedPipe() failed."); + abort(); + break; + } + } + + void completion_callback(bool success) + { + if (!success) + { +#ifdef DBUG_OFF + sql_print_warning("ConnectNamedPipe completed with %u", GetLastError()); +#endif + CloseHandle(m_handle); + m_handle= INVALID_HANDLE_VALUE; + begin_accept(); + return; + } + HANDLE pipe= m_handle; + begin_accept(); + // If threadpool is on, create connection in threadpool thread + if (!m_tp_env || !TrySubmitThreadpoolCallback(tp_create_pipe_connection, pipe, m_tp_env)) + create_pipe_connection(pipe); + } + + ~Pipe_Listener() + { + if (m_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(m_handle); + } + } + + static void cleanup() + { + LocalFree(pipe_security.lpSecurityDescriptor); + } +}; + +/** + Accept new client connections on Windows. + + Since we deal with pipe and sockets, they cannot be put into a select/loop. + But we can use asynchronous IO, and WaitForMultipleObject() loop. + + In addition, for slightly better performance, if we're using threadpool, + socket connections are accepted directly in the threadpool. + + The mode of operation is therefore + + 1. There is WaitForMultipleObject() loop that waits for shutdown notification + (hEventShutdown),and possibly pipes and sockets(e.g if threadpool is not used) + This loop ends when shutdown notification is detected. + + 2. If threadpool is used, new socket connections are accepted there. +*/ + + +#define MAX_WAIT_HANDLES 32 +#define NUM_PIPE_LISTENERS 24 +#define SHUTDOWN_IDX 0 +#define LISTENER_START_IDX 1 + +void handle_connections_win() +{ + Listener* all_listeners[MAX_WAIT_HANDLES]= {}; + HANDLE wait_events[MAX_WAIT_HANDLES]= {}; + int n_listeners= 0; + int n_waits= 0; + + Socket_Listener::init_winsock_extensions(); + + /* Listen for TCP connections on "extra-port" (no threadpool).*/ + if (extra_ip_sock.fd != INVALID_SOCKET) + all_listeners[n_listeners++]= new Socket_Listener(extra_ip_sock, 0); + + /* Listen for named pipe connections */ + if (mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe) + { + /* + Use several listeners for pipe, to reduce ERROR_PIPE_BUSY on client side. + */ + for (int i= 0; i < NUM_PIPE_LISTENERS; i++) + all_listeners[n_listeners++]= new Pipe_Listener(); + } + + if (base_ip_sock.fd != INVALID_SOCKET) + { + /* Wait for TCP connections.*/ + SetFileCompletionNotificationModes((HANDLE)base_ip_sock.fd, FILE_SKIP_SET_EVENT_ON_HANDLE); + all_listeners[n_listeners++]= new Socket_Listener(base_ip_sock, get_threadpool_win_callback_environ()); + } + + if (!n_listeners && !opt_bootstrap) + { + sql_print_error("Either TCP connections or named pipe connections must be enabled."); + unireg_abort(1); + } + + wait_events[SHUTDOWN_IDX]= hEventShutdown; + n_waits = 1; + + for (int i= 0; i < n_listeners; i++) + { + HANDLE wait_handle= all_listeners[i]->wait_handle(); + if(wait_handle) + { + DBUG_ASSERT((i == 0) || (all_listeners[i-1]->wait_handle() != 0)); + wait_events[n_waits++]= wait_handle; + } + all_listeners[i]->begin_accept(); + } + + for (;;) + { + DWORD idx = WaitForMultipleObjects(n_waits ,wait_events, FALSE, INFINITE); + DBUG_ASSERT((int)idx >= 0 && (int)idx < n_waits); + + if (idx == SHUTDOWN_IDX) + break; + + all_listeners[idx - LISTENER_START_IDX]->completion_callback(); + } + + /* Cleanup */ + for (int i= 0; i < n_listeners; i++) + { + Listener *listener= all_listeners[i]; + if (listener->wait_handle()) + delete listener; + else + // Threadpool-bound listener will be deleted in threadpool + // Do not call destructor, because callback maybe running. + listener->cancel(); + } + Pipe_Listener::cleanup(); +}
\ No newline at end of file diff --git a/sql/handle_connections_win.h b/sql/handle_connections_win.h new file mode 100644 index 00000000000..a81f4346fb2 --- /dev/null +++ b/sql/handle_connections_win.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2018 MariaDB Corporation. + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +/** + Handles incoming socket and pipe connections, on Windows. + Creates new (THD) connections.. +*/ +extern void handle_connections_win(); diff --git a/sql/handler.cc b/sql/handler.cc index 897d468f2ba..1b847e605ad 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -296,7 +296,7 @@ handler *get_ha_partition(partition_info *part_info) } else { - my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), + my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), static_cast<int>(sizeof(ha_partition))); } DBUG_RETURN(((handler*) partition)); @@ -2544,7 +2544,7 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, dummy_share.table_name= *alias; dummy_table.alias.set(alias->str, alias->length, table_alias_charset); file->change_table_ptr(&dummy_table, &dummy_share); - file->print_error(error, MYF(intercept ? ME_JUST_WARNING : 0)); + file->print_error(error, MYF(intercept ? ME_WARNING : 0)); } if (intercept) error= 0; @@ -3601,7 +3601,7 @@ void handler::print_error(int error, myf errflag) if (ha_thd()->transaction_rollback_request) { /* Ensure this becomes a true error */ - errflag&= ~(ME_JUST_WARNING | ME_JUST_INFO); + errflag&= ~(ME_WARNING | ME_NOTE); } int textno= -1; // impossible value @@ -3736,14 +3736,14 @@ void handler::print_error(int error, myf errflag) { textno=ER_RECORD_FILE_FULL; /* Write the error message to error log */ - errflag|= ME_NOREFRESH; + errflag|= ME_ERROR_LOG; break; } case HA_ERR_INDEX_FILE_FULL: { textno=ER_INDEX_FILE_FULL; /* Write the error message to error log */ - errflag|= ME_NOREFRESH; + errflag|= ME_ERROR_LOG; break; } case HA_ERR_LOCK_WAIT_TIMEOUT: @@ -3870,14 +3870,14 @@ void handler::print_error(int error, myf errflag) if (unlikely(fatal_error)) { /* Ensure this becomes a true error */ - errflag&= ~(ME_JUST_WARNING | ME_JUST_INFO); + errflag&= ~(ME_WARNING | ME_NOTE); if ((debug_assert_if_crashed_table || global_system_variables.log_warnings > 1)) { /* Log error to log before we crash or if extended warnings are requested */ - errflag|= ME_NOREFRESH; + errflag|= ME_ERROR_LOG; } } @@ -4049,7 +4049,8 @@ static bool update_frm_version(TABLE *table) int4store(version, MYSQL_VERSION_ID); - if ((result= (int)mysql_file_pwrite(file, (uchar*) version, 4, 51L, MYF_RW))) + if ((result= (int)mysql_file_pwrite(file, (uchar*) version, 4, 51L, + MYF(MY_WME+MY_NABP)))) goto err; table->s->mysql_version= MYSQL_VERSION_ID; @@ -4971,7 +4972,7 @@ int ha_create_table(THD *thd, const char *path, { if (!thd->is_error()) my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table_name, error); - table.file->print_error(error, MYF(ME_JUST_WARNING)); + table.file->print_error(error, MYF(ME_WARNING)); PSI_CALL_drop_table_share(temp_table, share.db.str, (uint)share.db.length, share.table_name.str, (uint)share.table_name.length); } diff --git a/sql/handler.h b/sql/handler.h index 788ac4dd474..6245bf7970f 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -3253,7 +3253,7 @@ public: /* True if changes to the table is persistent (no rollback) - This is manly used to decide how to log changes to the table in + This is mainly used to decide how to log changes to the table in the binary log. */ bool has_transactions() diff --git a/sql/init.h b/sql/init.h index e8dec0c1e2e..e96bb478cee 100644 --- a/sql/init.h +++ b/sql/init.h @@ -17,6 +17,6 @@ #define INIT_INCLUDED void unireg_init(ulong options); -ATTRIBUTE_NORETURN void unireg_end(void); +void unireg_end(void); #endif /* INIT_INCLUDED */ diff --git a/sql/item.cc b/sql/item.cc index 4eb47dc01c3..88bb929fc05 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -115,63 +115,20 @@ void Item::push_note_converted_to_positive_complement(THD *thd) } -longlong Item::val_datetime_packed_result() +longlong Item::val_datetime_packed_result(THD *thd) { MYSQL_TIME ltime, tmp; - if (get_date_result(<ime, TIME_FUZZY_DATES | TIME_INVALID_DATES)) + if (get_date_result(thd, <ime, Datetime::comparison_flags_for_get_date())) return 0; if (ltime.time_type != MYSQL_TIMESTAMP_TIME) return pack_time(<ime); - if ((null_value= time_to_datetime_with_warn(current_thd, <ime, &tmp, 0))) + if ((null_value= time_to_datetime_with_warn(thd, <ime, + &tmp, date_mode_t(0)))) return 0; return pack_time(&tmp); } -/** - Get date/time/datetime. - If DATETIME or DATE result is returned, it's converted to TIME. -*/ -bool Item::get_time_with_conversion(THD *thd, MYSQL_TIME *ltime, - ulonglong fuzzydate) -{ - if (get_date(ltime, fuzzydate)) - return true; - if (ltime->time_type != MYSQL_TIMESTAMP_TIME) - { - MYSQL_TIME ltime2; - if ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) && - (ltime->year || ltime->day || ltime->month)) - { - /* - Old mode conversion from DATETIME with non-zero YYYYMMDD part - to TIME works very inconsistently. Possible variants: - - truncate the YYYYMMDD part - - add (MM*33+DD)*24 to hours - - add (MM*31+DD)*24 to hours - Let's return TRUE here, to disallow equal field propagation. - Note, If we start to use this method in more pieces of the code other - than equal field propagation, we should probably return - TRUE only if some flag in fuzzydate is set. - */ - return true; - } - if (datetime_to_time_with_warn(thd, ltime, <ime2, TIME_SECOND_PART_DIGITS)) - { - /* - If the time difference between CURRENT_DATE and ltime - did not fit into the supported TIME range, then we set the - difference to the maximum possible value in the supported TIME range - */ - DBUG_ASSERT(0); - return (null_value= true); - } - *ltime= ltime2; - } - return false; -} - - /* For the items which don't have its own fast val_str_ascii() implementation we provide a generic slower version, @@ -253,36 +210,6 @@ String *Item::val_string_from_int(String *str) } -String *Item::val_string_from_decimal(String *str) -{ - my_decimal dec_buf, *dec= val_decimal(&dec_buf); - if (null_value) - return 0; - my_decimal_round(E_DEC_FATAL_ERROR, dec, decimals, FALSE, &dec_buf); - my_decimal2string(E_DEC_FATAL_ERROR, &dec_buf, 0, 0, 0, str); - return str; -} - - -/* - All val_xxx_from_date() must call this method, to expose consistent behaviour - regarding SQL_MODE when converting DATE/DATETIME to other data types. -*/ -bool Item::get_temporal_with_sql_mode(MYSQL_TIME *ltime) -{ - return get_date(ltime, field_type() == MYSQL_TYPE_TIME - ? TIME_TIME_ONLY - : sql_mode_for_dates(current_thd)); -} - - -bool Item::is_null_from_temporal() -{ - MYSQL_TIME ltime; - return get_temporal_with_sql_mode(<ime); -} - - longlong Item::val_int_from_str(int *error) { char buff[MAX_FIELD_WIDTH]; @@ -333,21 +260,6 @@ longlong Item::val_int_unsigned_typecast_from_int() } -String *Item::val_string_from_date(String *str) -{ - MYSQL_TIME ltime; - if (get_temporal_with_sql_mode(<ime) || - str->alloc(MAX_DATE_STRING_REP_LENGTH)) - { - null_value= 1; - return (String *) 0; - } - str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); - str->set_charset(&my_charset_numeric); - return str; -} - - my_decimal *Item::val_decimal_from_real(my_decimal *decimal_value) { double nr= val_real(); @@ -379,93 +291,10 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) } -my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value) -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_temporal_with_sql_mode(<ime)) - { - my_decimal_set_zero(decimal_value); - null_value= 1; // set NULL, stop processing - return 0; - } - return date2my_decimal(<ime, decimal_value); -} - - -my_decimal *Item::val_decimal_from_time(my_decimal *decimal_value) -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_time(<ime)) - { - my_decimal_set_zero(decimal_value); - return 0; - } - return date2my_decimal(<ime, decimal_value); -} - - -longlong Item::val_int_from_date() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_temporal_with_sql_mode(<ime)) - return 0; - longlong v= TIME_to_ulonglong(<ime); - return ltime.neg ? -v : v; -} - - -double Item::val_real_from_date() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_temporal_with_sql_mode(<ime)) - return 0; - return TIME_to_double(<ime); -} - - -double Item::val_real_from_decimal() -{ - /* Note that fix_fields may not be called for Item_avg_field items */ - double result; - my_decimal value_buff, *dec_val= val_decimal(&value_buff); - if (null_value) - return 0.0; - my_decimal2double(E_DEC_FATAL_ERROR, dec_val, &result); - return result; -} - - -longlong Item::val_int_from_decimal() -{ - /* Note that fix_fields may not be called for Item_avg_field items */ - longlong result; - my_decimal value, *dec_val= val_decimal(&value); - if (null_value) - return 0; - my_decimal2int(E_DEC_FATAL_ERROR, dec_val, unsigned_flag, &result); - return result; -} - - -longlong Item::val_int_unsigned_typecast_from_decimal() -{ - longlong result; - my_decimal tmp, *dec= val_decimal(&tmp); - if (null_value) - return 0; - my_decimal2int(E_DEC_FATAL_ERROR, dec, 1, &result); - return result; -} - - int Item::save_time_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; - if (get_time(<ime)) + if (get_time(field->table->in_use, <ime)) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); return field->store_time_dec(<ime, decimals); @@ -475,7 +304,8 @@ int Item::save_time_in_field(Field *field, bool no_conversions) int Item::save_date_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; - if (get_date(<ime, sql_mode_for_dates(field->table->in_use))) + THD *thd= field->table->in_use; + if (get_date(thd, <ime, sql_mode_for_dates(thd))) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); return field->store_time_dec(<ime, decimals); @@ -515,11 +345,11 @@ int Item::save_str_value_in_field(Field *field, String *result) Item::Item(THD *thd): is_expensive_cache(-1), rsize(0), name(null_clex_str), orig_name(0), - fixed(0), is_autogenerated_name(TRUE) + is_autogenerated_name(TRUE) { DBUG_ASSERT(thd); marker= 0; - maybe_null=null_value=with_sum_func=with_window_func=with_field=0; + maybe_null= null_value= with_window_func= with_field= false; in_rollup= 0; with_param= 0; @@ -563,11 +393,9 @@ Item::Item(THD *thd, Item *item): maybe_null(item->maybe_null), in_rollup(item->in_rollup), null_value(item->null_value), - with_sum_func(item->with_sum_func), with_param(item->with_param), with_window_func(item->with_window_func), with_field(item->with_field), - fixed(item->fixed), is_autogenerated_name(item->is_autogenerated_name) { next= thd->free_list; // Put in free list @@ -637,7 +465,6 @@ void Item::cleanup() { DBUG_ENTER("Item::cleanup"); DBUG_PRINT("enter", ("this: %p", this)); - fixed= 0; marker= 0; join_tab_idx= MAX_TABLES; if (orig_name) @@ -657,7 +484,7 @@ void Item::cleanup() bool Item::cleanup_processor(void *arg) { - if (fixed) + if (is_fixed()) cleanup(); return FALSE; } @@ -749,7 +576,8 @@ Item_ident::Item_ident(THD *thd, TABLE_LIST *view_arg, :Item_result_field(thd), orig_db_name(NullS), orig_table_name(view_arg->table_name.str), orig_field_name(*field_name_arg), - context(&view_arg->view->select_lex.context), + /* TODO: suspicious use of first_select_lex */ + context(&view_arg->view->first_select_lex()->context), db_name(NullS), table_name(view_arg->alias.str), field_name(*field_name_arg), alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX), @@ -943,12 +771,15 @@ bool Item_field::register_field_in_read_map(void *arg) { TABLE *table= (TABLE *) arg; int res= 0; + if (table && table != field->table) + return res; + if (field->vcol_info && - !bitmap_fast_test_and_set(field->table->vcol_set, field->field_index)) + !bitmap_fast_test_and_set(field->table->read_set, field->field_index)) { res= field->vcol_info->expr->walk(&Item::register_field_in_read_map,1,arg); } - if (field->table == table || !table) + else bitmap_set_bit(field->table->read_set, field->field_index); return res; } @@ -1176,7 +1007,7 @@ bool Item::check_type_scalar(const char *opname) const This hack in Item_outer_ref should probably be refactored eventually. Discuss with Sanja. */ - DBUG_ASSERT(fixed || type() == REF_ITEM); + DBUG_ASSERT(is_fixed() || type() == REF_ITEM); const Type_handler *handler= type_handler(); if (handler->is_scalar_type()) return false; @@ -1325,7 +1156,6 @@ Item *Item_cache::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) unlikely(!(cache= new (thd->mem_root) Item_cache_str(thd, conv)))) return NULL; // Safe conversion is not possible, or OEM cache->setup(thd, conv); - cache->fixed= false; // Make Item::fix_fields() happy return cache; } @@ -1376,7 +1206,7 @@ Item *Item::const_charset_converter(THD *thd, CHARSET_INFO *tocs, const char *func_name) { DBUG_ASSERT(const_item()); - DBUG_ASSERT(fixed); + DBUG_ASSERT(is_fixed()); StringBuffer<64>tmp; String *s= val_str(&tmp); MEM_ROOT *mem_root= thd->mem_root; @@ -1444,11 +1274,10 @@ Item *Item_param::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) As a extra convenience the time structure is reset on error or NULL values! */ -bool Item::get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item::get_date_from_int(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - longlong value= val_int(); - bool neg= !unsigned_flag && value < 0; - if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value, + Longlong_hybrid value(val_int(), unsigned_flag); + if (null_value || int_to_datetime_with_warn(thd, value, ltime, fuzzydate, field_name_or_null())) return null_value|= make_zero_date(ltime, fuzzydate); @@ -1456,60 +1285,29 @@ bool Item::get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate) } -bool Item::get_date_from_year(MYSQL_TIME *ltime, ulonglong fuzzydate) -{ - longlong value= val_int(); - DBUG_ASSERT(unsigned_flag || value >= 0); - if (max_length == 2) - { - if (value < 70) - value+= 2000; - else if (value <= 1900) - value+= 1900; - } - value*= 10000; /* make it YYYYMMHH */ - if (null_value || int_to_datetime_with_warn(false, value, - ltime, fuzzydate, - field_name_or_null())) - return null_value|= make_zero_date(ltime, fuzzydate); - return null_value= false; -} - - -bool Item::get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item::get_date_from_real(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { double value= val_real(); - if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, + if (null_value || double_to_datetime_with_warn(thd, value, ltime, fuzzydate, field_name_or_null())) return null_value|= make_zero_date(ltime, fuzzydate); return null_value= false; } -bool Item::get_date_from_decimal(MYSQL_TIME *ltime, ulonglong fuzzydate) -{ - my_decimal value, *res; - if (!(res= val_decimal(&value)) || - decimal_to_datetime_with_warn(res, ltime, fuzzydate, - field_name_or_null())) - return null_value|= make_zero_date(ltime, fuzzydate); - return null_value= false; -} - - -bool Item::get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item::get_date_from_string(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { char buff[40]; String tmp(buff,sizeof(buff), &my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), + str_to_datetime_with_warn(thd, res->charset(), res->ptr(), res->length(), ltime, fuzzydate)) return null_value|= make_zero_date(ltime, fuzzydate); return null_value= false; } -bool Item::make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item::make_zero_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { /* if the item was not null and convertion failed, we return a zero date @@ -1535,21 +1333,6 @@ bool Item::make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate) return !(fuzzydate & TIME_FUZZY_DATES); } -bool Item::get_seconds(ulonglong *sec, ulong *sec_part) -{ - if (decimals == 0) - { // optimize for an important special case - longlong val= val_int(); - bool neg= val < 0 && !unsigned_flag; - *sec= neg ? -val : val; - *sec_part= 0; - return neg; - } - my_decimal tmp, *dec= val_decimal(&tmp); - if (!dec) - return 0; - return my_decimal2seconds(dec, sec, sec_part); -} const MY_LOCALE *Item::locale_from_val_str() { @@ -1693,7 +1476,7 @@ Query_fragment::Query_fragment(THD *thd, sp_head *sphead, *****************************************************************************/ Item_sp_variable::Item_sp_variable(THD *thd, const LEX_CSTRING *sp_var_name) - :Item(thd), m_thd(0), m_name(*sp_var_name) + :Item_fixed_hybrid(thd), m_thd(0), m_name(*sp_var_name) #ifndef DBUG_OFF , m_sp(0) #endif @@ -1705,7 +1488,7 @@ bool Item_sp_variable::fix_fields_from_item(THD *thd, Item **, const Item *it) { m_thd= thd; /* NOTE: this must be set before any this_xxx() */ - DBUG_ASSERT(it->fixed); + DBUG_ASSERT(it->is_fixed()); max_length= it->max_length; decimals= it->decimals; @@ -1786,11 +1569,11 @@ my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value) } -bool Item_sp_variable::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_sp_variable::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed); Item *it= this_item(); - bool val= it->get_date(ltime, fuzzydate); + bool val= it->get_date(thd, ltime, fuzzydate); null_value= it->null_value; return val; } @@ -1826,10 +1609,10 @@ Item_splocal::Item_splocal(THD *thd, Rewritable_query_parameter(pos_in_q, len_in_q), Type_handler_hybrid_field_type(handler), m_rcontext_handler(rh), - m_var_idx(sp_var_idx) + m_var_idx(sp_var_idx), + m_type(handler == &type_handler_row ? ROW_ITEM : CONST_ITEM) { maybe_null= TRUE; - m_type= sp_map_item_type(handler); } @@ -2167,10 +1950,10 @@ my_decimal *Item_name_const::val_decimal(my_decimal *decimal_value) return val; } -bool Item_name_const::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_name_const::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed); - bool rc= value_item->get_date(ltime, fuzzydate); + bool rc= value_item->get_date(thd, ltime, fuzzydate); null_value= value_item->null_value; return rc; } @@ -2182,54 +1965,25 @@ bool Item_name_const::is_null() Item_name_const::Item_name_const(THD *thd, Item *name_arg, Item *val): - Item(thd), value_item(val), name_item(name_arg) + Item_fixed_hybrid(thd), value_item(val), name_item(name_arg) { Item::maybe_null= TRUE; - valid_args= true; - if (!name_item->basic_const_item()) - goto err; - - if (value_item->basic_const_item()) - return; // ok - - if (value_item->type() == FUNC_ITEM) - { - Item_func *value_func= (Item_func *) value_item; - if (value_func->functype() != Item_func::COLLATE_FUNC && - value_func->functype() != Item_func::NEG_FUNC) - goto err; - - if (value_func->key_item()->basic_const_item()) - return; // ok - } - -err: - valid_args= false; - my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST"); } Item::Type Item_name_const::type() const { /* - As - 1. one can try to create the Item_name_const passing non-constant - arguments, although it's incorrect and - 2. the type() method can be called before the fix_fields() to get - type information for a further type cast, e.g. - if (item->type() == FIELD_ITEM) - ((Item_field *) item)->... - we return NULL_ITEM in the case to avoid wrong casting. - - valid_args guarantees value_item->basic_const_item(); if type is - FUNC_ITEM, then we have a fudged item_func_neg() on our hands - and return the underlying type. + + We are guarenteed that value_item->basic_const_item(), if not + an error is thrown that WRONG ARGUMENTS are supplied to + NAME_CONST function. + If type is FUNC_ITEM, then we have a fudged item_func_neg() + on our hands and return the underlying type. For Item_func_set_collation() e.g. NAME_CONST('name', 'value' COLLATE collation) we return its 'value' argument type. */ - if (!valid_args) - return NULL_ITEM; Item::Type value_type= value_item->type(); if (value_type == FUNC_ITEM) { @@ -2372,7 +2126,7 @@ void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array, else { /* Not a SUM() function */ - if (unlikely((!with_sum_func && !(split_flags & SPLIT_SUM_SELECT)))) + if (unlikely((!with_sum_func() && !(split_flags & SPLIT_SUM_SELECT)))) { /* This is not a SUM function and there are no SUM functions inside. @@ -2380,7 +2134,7 @@ void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array, */ return; } - if (likely(with_sum_func || + if (likely(with_sum_func() || (type() == FUNC_ITEM && (((Item_func *) this)->functype() == Item_func::ISNOTNULLTEST_FUNC || @@ -2755,7 +2509,7 @@ bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll, else thd->change_item_tree(arg, conv); - if (conv->fix_fields(thd, arg)) + if (conv->fix_fields_if_needed(thd, arg)) { res= TRUE; break; // we cannot return here, we need to restore "arena". @@ -2904,7 +2658,7 @@ bool Item_sp::execute(THD *thd, bool *null_value, Item **args, uint arg_count) if (unlikely(execute_impl(thd, args, arg_count))) { *null_value= 1; - context->process_error(thd); + process_error(thd); if (thd->killed) thd->send_kill_message(); return true; @@ -2937,7 +2691,7 @@ Item_sp::execute_impl(THD *thd, Item **args, uint arg_count) DBUG_ENTER("Item_sp::execute_impl"); - if (context->security_ctx) + if (context && context->security_ctx) { /* Set view definer security context */ thd->security_ctx= context->security_ctx; @@ -3116,7 +2870,10 @@ Item_field::Item_field(THD *thd, Field *f) have_privileges(0), any_privileges(0) { set_field(f); - + /* + field_name and table_name should not point to garbage + if this item is to be reused + */ orig_table_name= table_name; orig_field_name= field_name; with_field= 1; @@ -3433,7 +3190,7 @@ String *Item_field::str_result(String *str) return result_field->val_str(str,&str_value); } -bool Item_field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_field::get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate) { if ((null_value=field->is_null()) || field->get_date(ltime,fuzzydate)) { @@ -3443,7 +3200,7 @@ bool Item_field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) return 0; } -bool Item_field::get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_field::get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (result_field->is_null() || result_field->get_date(ltime,fuzzydate)) { @@ -3652,6 +3409,48 @@ longlong Item_field::val_int_endpoint(bool left_endp, bool *incl_endp) return null_value? LONGLONG_MIN : res; } + +bool Item_basic_value::eq(const Item *item, bool binary_cmp) const +{ + const Item_const *c0, *c1; + const Type_handler *h0, *h1; + /* + - Test get_item_const() for NULL filters out Item_param + bound in a way that needs a data type conversion + (e.g. non-integer value in a LIMIT clause). + Item_param::get_item_const() return NULL in such cases. + - Test for type_handler_for_comparison() equality makes sure + that values of different data type groups do not get detected + as equal (e.g. numbers vs strings, time vs datetime). + - Test for cast_to_int_type_handler() equality distinguishes + values with dual properties. For example, VARCHAR 'abc' and hex + hybrid 0x616263 are equal in string context, but they are not equal + if the hybrid appears in integer context (it behaves as integer then). + Here we have no full information about the context, so treat them + as not equal. + QQ: We could pass Value_source::Context here instead of + "bool binary_cmp", to make substitution more delicate. + See Field::get_equal_const_item(). + */ + bool res= (c0= get_item_const()) && + (c1= item->get_item_const()) && + (h0= type_handler())->type_handler_for_comparison() == + (h1= item->type_handler())->type_handler_for_comparison() && + h0->cast_to_int_type_handler()->type_handler_for_comparison() == + h1->cast_to_int_type_handler()->type_handler_for_comparison() && + h0->Item_const_eq(c0, c1, binary_cmp); + DBUG_EXECUTE_IF("Item_basic_value", + push_warning_printf(current_thd, + Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, "%seq=%d a=%s b=%s", + binary_cmp ? "bin_" : "", (int) res, + DbugStringItemTypeValue(current_thd, this).c_ptr(), + DbugStringItemTypeValue(current_thd, item).c_ptr() + );); + return res; +} + + /** Create an item from a string we KNOW points to a valid longlong end \\0 terminated number string. @@ -3671,7 +3470,6 @@ Item_int::Item_int(THD *thd, const char *str_arg, size_t length): the field name. */ name.length= !str_arg[max_length] ? max_length : strlen(str_arg); - fixed= 1; } @@ -3683,8 +3481,6 @@ my_decimal *Item_int::val_decimal(my_decimal *decimal_value) String *Item_int::val_str(String *str) { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); str->set_int(value, unsigned_flag, collation.collation); return str; } @@ -3721,8 +3517,6 @@ Item_uint::Item_uint(THD *thd, const char *str_arg, longlong i, uint length): String *Item_uint::val_str(String *str) { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); str->set((ulonglong) value, collation.collation); return str; } @@ -3744,7 +3538,6 @@ Item_decimal::Item_decimal(THD *thd, const char *str_arg, size_t length, name.str= str_arg; name.length= safe_strlen(str_arg); decimals= (uint8) decimal_value.frac; - fixed= 1; max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg + decimals, decimals, @@ -3756,7 +3549,6 @@ Item_decimal::Item_decimal(THD *thd, longlong val, bool unsig): { int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value); decimals= (uint8) decimal_value.frac; - fixed= 1; max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg + decimals, decimals, @@ -3769,7 +3561,6 @@ Item_decimal::Item_decimal(THD *thd, double val, int precision, int scale): { double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value); decimals= (uint8) decimal_value.frac; - fixed= 1; max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg + decimals, decimals, @@ -3786,16 +3577,14 @@ Item_decimal::Item_decimal(THD *thd, const char *str, const my_decimal *val_arg, name.length= safe_strlen(str); decimals= (uint8) decimal_par; max_length= length; - fixed= 1; } -Item_decimal::Item_decimal(THD *thd, my_decimal *value_par): +Item_decimal::Item_decimal(THD *thd, const my_decimal *value_par): Item_num(thd) { my_decimal2decimal(value_par, &decimal_value); decimals= (uint8) decimal_value.frac; - fixed= 1; max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg + decimals, decimals, @@ -3804,63 +3593,15 @@ Item_decimal::Item_decimal(THD *thd, my_decimal *value_par): Item_decimal::Item_decimal(THD *thd, const uchar *bin, int precision, int scale): - Item_num(thd) + Item_num(thd), + decimal_value(bin, precision, scale) { - binary2my_decimal(E_DEC_FATAL_ERROR, bin, - &decimal_value, precision, scale); decimals= (uint8) decimal_value.frac; - fixed= 1; max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); } -longlong Item_decimal::val_int() -{ - longlong result; - my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &result); - return result; -} - -double Item_decimal::val_real() -{ - double result; - my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result); - return result; -} - -String *Item_decimal::val_str(String *result) -{ - result->set_charset(&my_charset_numeric); - my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, result); - return result; -} - -void Item_decimal::print(String *str, enum_query_type query_type) -{ - my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, &str_value); - str->append(str_value); -} - - -bool Item_decimal::eq(const Item *item, bool binary_cmp) const -{ - if (type() == item->type() && item->basic_const_item()) - { - /* - We need to cast off const to call val_decimal(). This should - be OK for a basic constant. Additionally, we can pass 0 as - a true decimal constant will return its internal decimal - storage and ignore the argument. - */ - Item *arg= (Item*) item; - my_decimal *value= arg->val_decimal(0); - return !my_decimal_cmp(&decimal_value, value); - } - return 0; -} - - void Item_decimal::set_decimal_value(my_decimal *value_par) { my_decimal2decimal(value_par, &decimal_value); @@ -3882,8 +3623,6 @@ Item *Item_decimal::clone_item(THD *thd) String *Item_float::val_str(String *str) { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); str->set_real(value, decimals, &my_charset_numeric); return str; } @@ -3891,8 +3630,6 @@ String *Item_float::val_str(String *str) my_decimal *Item_float::val_decimal(my_decimal *decimal_value) { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_value); return (decimal_value); } @@ -3953,7 +3690,6 @@ void Item_string::print(String *str, enum_query_type query_type) double Item_string::val_real() { - DBUG_ASSERT(fixed == 1); return double_from_string_with_check(&str_value); } @@ -3964,7 +3700,6 @@ double Item_string::val_real() */ longlong Item_string::val_int() { - DBUG_ASSERT(fixed == 1); return longlong_from_string_with_check(&str_value); } @@ -3977,23 +3712,17 @@ my_decimal *Item_string::val_decimal(my_decimal *decimal_value) double Item_null::val_real() { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); null_value=1; return 0.0; } longlong Item_null::val_int() { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); null_value=1; return 0; } /* ARGSUSED */ String *Item_null::val_str(String *str) { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); null_value=1; return 0; } @@ -4004,10 +3733,8 @@ my_decimal *Item_null::val_decimal(my_decimal *decimal_value) } -bool Item_null::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_null::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); make_zero_date(ltime, fuzzydate); return (null_value= true); } @@ -4057,8 +3784,6 @@ Item_param::Item_param(THD *thd, const LEX_CSTRING *name_arg, */ Type_handler_hybrid_field_type(&type_handler_null), state(NO_VALUE), - /* Don't pretend to be a literal unless value for this item is set. */ - item_type(PARAM_ITEM), m_empty_string_is_null(false), indicator(STMT_INDICATOR_NONE), m_out_param_info(NULL), @@ -4117,7 +3842,6 @@ void Item_param::sync_clones() c->Type_geometry_attributes::operator=(*this); c->state= state; - c->item_type= item_type; c->m_empty_string_is_null= m_empty_string_is_null; c->value.PValue_simple::operator=(value); @@ -4152,7 +3876,6 @@ void Item_param::set_null() max_length= 0; decimals= 0; state= NULL_VALUE; - fix_type(Item::NULL_ITEM); DBUG_VOID_RETURN; } @@ -4167,7 +3890,6 @@ void Item_param::set_int(longlong i, uint32 max_length_arg) decimals= 0; maybe_null= 0; null_value= 0; - fix_type(Item::INT_ITEM); DBUG_VOID_RETURN; } @@ -4182,7 +3904,6 @@ void Item_param::set_double(double d) decimals= NOT_FIXED_DEC; maybe_null= 0; null_value= 0; - fix_type(Item::REAL_ITEM); DBUG_VOID_RETURN; } @@ -4215,7 +3936,6 @@ void Item_param::set_decimal(const char *str, ulong length) decimals, unsigned_flag); maybe_null= 0; null_value= 0; - fix_type(Item::DECIMAL_ITEM); DBUG_VOID_RETURN; } @@ -4233,7 +3953,6 @@ void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg) decimals, unsigned_flag); maybe_null= 0; null_value= 0; - fix_type(Item::DECIMAL_ITEM); } @@ -4245,7 +3964,6 @@ void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg) decimals= decimals_arg; maybe_null= 0; null_value= 0; - fix_type(Item::DATE_ITEM); } @@ -4330,7 +4048,6 @@ bool Item_param::set_str(const char *str, ulong length, null_value= 0; /* max_length and decimals are set after charset conversion */ /* sic: str may be not null-terminated, don't add DBUG_PRINT here */ - fix_type(Item::STRING_ITEM); DBUG_RETURN(FALSE); } @@ -4364,7 +4081,6 @@ bool Item_param::set_longdata(const char *str, ulong length) state= LONG_DATA_VALUE; maybe_null= 0; null_value= 0; - fix_type(Item::STRING_ITEM); DBUG_RETURN(FALSE); } @@ -4428,7 +4144,7 @@ bool Item_param::set_from_item(THD *thd, Item *item) } } struct st_value tmp; - if (!item->save_in_value(&tmp)) + if (!item->save_in_value(thd, &tmp)) { const Type_handler *h= item->type_handler(); set_handler(h); @@ -4466,16 +4182,6 @@ void Item_param::reset() state= NO_VALUE; maybe_null= 1; null_value= 0; - fixed= false; - /* - Don't reset item_type to PARAM_ITEM: it's only needed to guard - us from item optimizations at prepare stage, when item doesn't yet - contain a literal of some kind. - In all other cases when this object is accessed its value is - set (this assumption is guarded by 'state' and - DBUG_ASSERTS(state != NO_VALUE) in all Item_param::get_* - methods). - */ DBUG_VOID_RETURN; } @@ -4541,7 +4247,7 @@ void Item_param::invalid_default_param() const } -bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate) +bool Item_param::get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate) { /* LIMIT clause parameter should not call get_date() @@ -4555,7 +4261,7 @@ bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate) *res= value.time; return 0; } - return type_handler()->Item_get_date(this, res, fuzzydate); + return type_handler()->Item_get_date(thd, this, res, fuzzydate); } @@ -4567,11 +4273,7 @@ double Item_param::PValue::val_real() const case INT_RESULT: return (double) integer; case DECIMAL_RESULT: - { - double result; - my_decimal2double(E_DEC_FATAL_ERROR, &m_decimal, &result); - return result; - } + return m_decimal.to_double(); case STRING_RESULT: return double_from_string_with_check(&m_string); case TIME_RESULT: @@ -4596,11 +4298,7 @@ longlong Item_param::PValue::val_int(const Type_std_attributes *attr) const case INT_RESULT: return integer; case DECIMAL_RESULT: - { - longlong i; - my_decimal2int(E_DEC_FATAL_ERROR, &m_decimal, attr->unsigned_flag, &i); - return i; - } + return m_decimal.to_longlong(attr->unsigned_flag); case STRING_RESULT: return longlong_from_string_with_check(&m_string); case TIME_RESULT: @@ -4650,7 +4348,7 @@ String *Item_param::PValue::val_str(String *str, str->set(integer, &my_charset_bin); return str; case DECIMAL_RESULT: - if (my_decimal2string(E_DEC_FATAL_ERROR, &m_decimal, 0, 0, 0, str) <= 1) + if (m_decimal.to_string_native(str, 0, 0, 0) <= 1) return str; return NULL; case TIME_RESULT: @@ -4691,8 +4389,7 @@ const String *Item_param::value_query_val_str(THD *thd, String *str) const str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin); return str; case DECIMAL_RESULT: - if (my_decimal2string(E_DEC_FATAL_ERROR, &value.m_decimal, - 0, 0, 0, str) > 1) + if (value.m_decimal.to_string_native(str, 0, 0, 0) > 1) return &my_null_string; return str; case TIME_RESULT: @@ -4797,11 +4494,20 @@ bool Item_param::convert_str_value(THD *thd) bool Item_param::basic_const_item() const { - DBUG_ASSERT(fixed || state == NO_VALUE); - if (state == NO_VALUE || - (state == SHORT_DATA_VALUE && type_handler()->cmp_type() == TIME_RESULT)) - return FALSE; - return TRUE; + switch (state) { + case LONG_DATA_VALUE: + case NULL_VALUE: + return true; + case SHORT_DATA_VALUE: + return type_handler()->cmp_type() != TIME_RESULT; + case DEFAULT_VALUE: + case IGNORE_VALUE: + invalid_default_param(); + return false; + case NO_VALUE: + break; + } + return false; } @@ -4862,48 +4568,6 @@ Item_param::clone_item(THD *thd) } -bool Item_param::value_eq(const Item *item, bool binary_cmp) const -{ - switch (value.type_handler()->cmp_type()) { - case INT_RESULT: - return int_eq(value.integer, item); - case REAL_RESULT: - return real_eq(value.real, item); - case STRING_RESULT: - return str_eq(&value.m_string, item, binary_cmp); - case DECIMAL_RESULT: - case TIME_RESULT: - case ROW_RESULT: - break; - } - return false; -} - - -bool -Item_param::eq(const Item *item, bool binary_cmp) const -{ - if (!basic_const_item()) - return FALSE; - - // There's no "default". See comments in Item_param::save_in_field(). - switch (state) { - case IGNORE_VALUE: - case DEFAULT_VALUE: - invalid_default_param(); - return false; - case NULL_VALUE: - return null_eq(item); - case SHORT_DATA_VALUE: - case LONG_DATA_VALUE: - return value_eq(item, binary_cmp); - case NO_VALUE: - return false; - } - DBUG_ASSERT(0); // Garbage - return FALSE; -} - /* End of Item_param related */ void Item_param::print(String *str, enum_query_type query_type) @@ -4957,12 +4621,10 @@ Item_param::set_param_type_and_swap_value(Item_param *src) { Type_std_attributes::set(src); set_handler(src->type_handler()); - item_type= src->item_type; maybe_null= src->maybe_null; null_value= src->null_value; state= src->state; - fixed= src->fixed; value.swap(src->value); } @@ -4972,7 +4634,6 @@ void Item_param::set_default() { m_is_settable_routine_parameter= false; state= DEFAULT_VALUE; - fixed= true; /* When Item_param is set to DEFAULT_VALUE: - its val_str() and val_decimal() return NULL @@ -4988,7 +4649,6 @@ void Item_param::set_ignore() { m_is_settable_routine_parameter= false; state= IGNORE_VALUE; - fixed= true; null_value= true; } @@ -5017,7 +4677,7 @@ Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it) correctly fetches the value from the client-server protocol, using set_param_func(). */ - if (arg->save_in_value(&tmp) || + if (arg->save_in_value(thd, &tmp) || set_value(thd, arg, &tmp, arg->type_handler())) { set_null(); @@ -5163,17 +4823,6 @@ my_decimal *Item_copy_string::val_decimal(my_decimal *decimal_value) Functions to convert item to field (for send_result_set_metadata) */ -/* ARGSUSED */ -bool Item::fix_fields(THD *thd, Item **ref) -{ - - // We do not check fields which are fixed during construction - DBUG_ASSERT(fixed == 0 || basic_const_item()); - fixed= 1; - return FALSE; -} - - void Item_ref_null_helper::save_val(Field *to) { DBUG_ASSERT(fixed == 1); @@ -5227,9 +4876,9 @@ String* Item_ref_null_helper::val_str(String* s) } -bool Item_ref_null_helper::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_ref_null_helper::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return (owner->was_null|= null_value= (*ref)->get_date_result(ltime, fuzzydate)); + return (owner->was_null|= null_value= (*ref)->get_date_result(thd, ltime, fuzzydate)); } @@ -5522,7 +5171,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) ref->alias_name_used= TRUE; /* If this is a non-aggregated field inside HAVING, search in GROUP BY. */ - if (select->having_fix_field && !ref->with_sum_func && group_list) + if (select->having_fix_field && !ref->with_sum_func() && group_list) { group_by_ref= find_field_in_group_list(ref, group_list); @@ -5564,7 +5213,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) ref->name.str, "forward reference in item list"); return NULL; } - DBUG_ASSERT((*select_ref)->fixed); + DBUG_ASSERT((*select_ref)->is_fixed()); return &select->ref_pointer_array[counter]; } if (group_by_ref) @@ -5687,7 +5336,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) Name_resolution_context *outer_context= 0; SELECT_LEX *select= 0; /* Currently derived tables cannot be correlated */ - if (current_sel->master_unit()->first_select()->linkage != + if (current_sel->master_unit()->first_select()->get_linkage() != DERIVED_TABLE_TYPE) outer_context= context->outer_context; @@ -5841,7 +5490,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) return -1; /* Some error occurred (e.g. ambiguous names). */ if (ref != not_found_item) { - DBUG_ASSERT(*ref && (*ref)->fixed); + DBUG_ASSERT(*ref && (*ref)->is_fixed()); prev_subselect_item->used_tables_and_const_cache_join(*ref); break; } @@ -5883,7 +5532,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) Item_ref *rf; /* Should have been checked in resolve_ref_in_select_and_group(). */ - DBUG_ASSERT(*ref && (*ref)->fixed); + DBUG_ASSERT(*ref && (*ref)->is_fixed()); /* Here, a subset of actions performed by Item_ref::set_properties is not enough. So we pass ptr to NULL into Item_[direct]_ref @@ -6459,7 +6108,7 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg) comparison context, and it's safe to replace it to the constant from item_equal. */ - DBUG_ASSERT(type_handler()->type_handler_for_comparison()->cmp_type() == + DBUG_ASSERT(type_handler_for_comparison()->cmp_type() == item_equal->compare_type_handler()->cmp_type()); return const_item2; } @@ -6831,12 +6480,11 @@ int Item::save_real_in_field(Field *field, bool no_conversions) int Item::save_decimal_in_field(Field *field, bool no_conversions) { - my_decimal decimal_value; - my_decimal *value= val_decimal(&decimal_value); - if (null_value) + VDec value(this); + if (value.is_null()) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); - return field->store_decimal(value); + return field->store_decimal(value.ptr()); } @@ -6903,14 +6551,18 @@ Item_string::make_string_literal_concat(THD *thd, const LEX_CSTRING *str) */ Item *Item_string::make_odbc_literal(THD *thd, const LEX_CSTRING *typestr) { - enum_field_types type= odbc_temporal_literal_type(typestr); - Item *res= type == MYSQL_TYPE_STRING ? this : - create_temporal_literal(thd, val_str(NULL), type, false); + Item_literal *res; + const Type_handler *h; + if (collation.repertoire == MY_REPERTOIRE_ASCII && + str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4 && + (h= Type_handler::odbc_literal_type_handler(typestr)) && + (res= h->create_literal_item(thd, val_str(NULL), false))) + return res; /* - create_temporal_literal() returns NULL if failed to parse the string, + h->create_literal_item() returns NULL if failed to parse the string, or the string format did not match the type, e.g.: {d'2001-01-01 10:10:10'} */ - return res ? res : this; + return this; } @@ -7113,7 +6765,6 @@ Item_float::Item_float(THD *thd, const char *str_arg, size_t length): name.length= strlen(str_arg); decimals=(uint8) nr_of_decimals(str_arg, str_arg+length); max_length=(uint32)length; - fixed= 1; } @@ -7169,7 +6820,6 @@ void Item_hex_constant::hex_string_init(THD *thd, const char *str, size_t str_le } *ptr=0; // Keep purify happy collation.set(&my_charset_bin, DERIVATION_COERCIBLE); - fixed= 1; unsigned_flag= 1; } @@ -7248,19 +6898,9 @@ Item_bin_string::Item_bin_string(THD *thd, const char *str, size_t str_length): ptr[0]= 0; collation.set(&my_charset_bin, DERIVATION_COERCIBLE); - fixed= 1; } -bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const -{ - return - item->basic_const_item() && type() == item->type() && - field_type() == ((Item_temporal_literal *) item)->field_type() && - !my_time_compare(&cached_time, - &((Item_temporal_literal *) item)->cached_time); -} - void Item_date_literal::print(String *str, enum_query_type query_type) { str->append("DATE'"); @@ -7277,12 +6917,11 @@ Item *Item_date_literal::clone_item(THD *thd) } -bool Item_date_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_date_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - DBUG_ASSERT(fixed); - fuzzy_date |= sql_mode_for_dates(current_thd); + fuzzydate |= sql_mode_for_dates(thd); *ltime= cached_time; - return (null_value= check_date_with_warn(ltime, fuzzy_date, + return (null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)); } @@ -7303,12 +6942,11 @@ Item *Item_datetime_literal::clone_item(THD *thd) } -bool Item_datetime_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_datetime_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - DBUG_ASSERT(fixed); - fuzzy_date |= sql_mode_for_dates(current_thd); + fuzzydate |= sql_mode_for_dates(thd); *ltime= cached_time; - return (null_value= check_date_with_warn(ltime, fuzzy_date, + return (null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)); } @@ -7329,13 +6967,12 @@ Item *Item_time_literal::clone_item(THD *thd) } -bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_time_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - DBUG_ASSERT(fixed); *ltime= cached_time; - if (fuzzy_date & TIME_TIME_ONLY) + if (fuzzydate & TIME_TIME_ONLY) return (null_value= false); - return (null_value= check_date_with_warn(ltime, fuzzy_date, + return (null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)); } @@ -7451,7 +7088,7 @@ void Item_field::update_null_value() no_errors= thd->no_errors; thd->no_errors= 1; - Item::update_null_value(); + type_handler()->Item_update_null_value(this); thd->no_errors= no_errors; } @@ -7502,6 +7139,231 @@ Item *Item_field::update_value_transformer(THD *thd, uchar *select_arg) } +/** + @brief + Prepare AND/OR formula for extraction of a pushable condition + + @param checker the checker callback function to be applied to the nodes + of the tree of the object + @param arg parameter to be passed to the checker + + @details + This method recursively traverses this AND/OR condition and for each + subformula of the condition it checks whether it can be usable for the + extraction of a pushable condition. The criteria of pushability of + a subformula is checked by the callback function 'checker' with one + parameter arg. The subformulas that are not usable are marked with + the flag NO_EXTRACTION_FL. + @note + This method is called before any call of build_pushable_cond. + The flag NO_EXTRACTION_FL set in a subformula allows to avoid building + clones for the subformulas that are not used in the pushable condition. + @note + This method is called for pushdown conditions into materialized + derived tables/views optimization. + Item::pushable_cond_checker_for_derived() is passed as the actual callback + function. + Also it is called for pushdown conditions in materialized IN subqueries. + Item::pushable_cond_checker_for_subquery is passed as the actual + callback function. +*/ + +void Item::check_pushable_cond(Pushdown_checker checker, uchar *arg) +{ + clear_extraction_flag(); + if (type() == Item::COND_ITEM) + { + bool and_cond= ((Item_cond*) this)->functype() == Item_func::COND_AND_FUNC; + List_iterator<Item> li(*((Item_cond*) this)->argument_list()); + uint count= 0; + Item *item; + while ((item=li++)) + { + item->check_pushable_cond(checker, arg); + if (item->get_extraction_flag() != NO_EXTRACTION_FL) + count++; + else if (!and_cond) + break; + } + if ((and_cond && count == 0) || item) + { + set_extraction_flag(NO_EXTRACTION_FL); + if (and_cond) + li.rewind(); + while ((item= li++)) + item->clear_extraction_flag(); + } + } + else if (!((this->*checker) (arg))) + set_extraction_flag(NO_EXTRACTION_FL); +} + + +/** + @brief + Build condition extractable from this condition for pushdown + + @param thd the thread handle + @param checker the checker callback function to be applied to the + equal items of multiple equality items + @param arg parameter to be passed to the checker + + @details + This method finds out what condition that can be pushed down can be + extracted from this condition. If such condition C exists the + method builds the item for it. The method uses the flag NO_EXTRACTION_FL + set by the preliminary call of the method check_pushable_cond() to figure + out whether a subformula is pushable or not. + In the case when this item is a multiple equality a checker method is + called to find the equal fields to build a new equality that can be + pushed down. + @note + The built condition C is always implied by the condition cond + (cond => C). The method tries to build the most restrictive such + condition (i.e. for any other condition C' such that cond => C' + we have C => C'). + @note + The build item is not ready for usage: substitution for the field items + has to be done and it has to be re-fixed. + @note + This method is called for pushdown conditions into materialized + derived tables/views optimization. + Item::pushable_equality_checker_for_derived() is passed as the actual + callback function. + Also it is called for pushdown conditions into materialized IN subqueries. + Item::pushable_equality_checker_for_subquery() is passed as the actual + callback function. + + @retval + the built condition pushable into if such a condition exists + NULL if there is no such a condition +*/ + +Item *Item::build_pushable_cond(THD *thd, + Pushdown_checker checker, + uchar *arg) +{ + bool is_multiple_equality= type() == Item::FUNC_ITEM && + ((Item_func*) this)->functype() == Item_func::MULT_EQUAL_FUNC; + + if (get_extraction_flag() == NO_EXTRACTION_FL) + return 0; + + if (type() == Item::COND_ITEM) + { + bool cond_and= false; + Item_cond *new_cond; + if (((Item_cond*) this)->functype() == Item_func::COND_AND_FUNC) + { + cond_and= true; + new_cond= new (thd->mem_root) Item_cond_and(thd); + } + else + new_cond= new (thd->mem_root) Item_cond_or(thd); + if (!new_cond) + return 0; + List_iterator<Item> li(*((Item_cond*) this)->argument_list()); + Item *item; + bool is_fix_needed= false; + + while ((item=li++)) + { + if (item->get_extraction_flag() == NO_EXTRACTION_FL) + { + if (!cond_and) + return 0; + continue; + } + Item *fix= item->build_pushable_cond(thd, checker, arg); + if (!fix && !cond_and) + return 0; + if (!fix) + continue; + + if (fix->type() == Item::COND_ITEM && + ((Item_cond*) fix)->functype() == Item_func::COND_AND_FUNC) + is_fix_needed= true; + + if (new_cond->argument_list()->push_back(fix, thd->mem_root)) + return 0; + } + if (is_fix_needed && new_cond->fix_fields(thd, 0)) + return 0; + + switch (new_cond->argument_list()->elements) + { + case 0: + return 0; + case 1: + return new_cond->argument_list()->head(); + default: + return new_cond; + } + } + else if (is_multiple_equality) + { + Item *new_cond= NULL; + int i= 0; + Item_equal *item_equal= (Item_equal *) this; + Item *left_item = item_equal->get_const(); + Item_equal_fields_iterator it(*item_equal); + Item *item; + Item *right_item; + if (!left_item) + { + while ((item=it++)) + { + left_item= ((item->*checker) (arg)) ? item : NULL; + if (left_item) + break; + } + } + if (!left_item) + return 0; + while ((item=it++)) + { + right_item= ((item->*checker) (arg)) ? item : NULL; + if (!right_item) + continue; + Item_func_eq *eq= 0; + Item *left_item_clone= left_item->build_clone(thd); + Item *right_item_clone= item->build_clone(thd); + if (left_item_clone && right_item_clone) + { + left_item_clone->set_item_equal(NULL); + right_item_clone->set_item_equal(NULL); + eq= new (thd->mem_root) Item_func_eq(thd, right_item_clone, + left_item_clone); + } + if (eq) + { + i++; + switch (i) + { + case 1: + new_cond= eq; + break; + case 2: + new_cond= new (thd->mem_root) Item_cond_and(thd, new_cond, eq); + break; + default: + if (((Item_cond_and*)new_cond)->argument_list()->push_back(eq, + thd->mem_root)) + return 0; + break; + } + } + } + if (new_cond && new_cond->fix_fields(thd, &new_cond)) + return 0; + return new_cond; + } + else if (get_extraction_flag() != NO_EXTRACTION_FL) + return build_clone(thd); + return 0; +} + + static Item *get_field_item_for_having(THD *thd, Item *item, st_select_lex *sel) { @@ -7621,18 +7483,18 @@ Item *Item_direct_view_ref::derived_field_transformer_for_where(THD *thd, } static -Grouping_tmp_field *find_matching_grouping_field(Item *item, - st_select_lex *sel) +Field_pair *find_matching_grouping_field(Item *item, + st_select_lex *sel) { DBUG_ASSERT(item->type() == Item::FIELD_ITEM || (item->type() == Item::REF_ITEM && ((Item_ref *) item)->ref_type() == Item_ref::VIEW_REF)); - List_iterator<Grouping_tmp_field> li(sel->grouping_tmp_fields); - Grouping_tmp_field *gr_field; + List_iterator<Field_pair> li(sel->grouping_tmp_fields); + Field_pair *gr_field; Item_field *field_item= (Item_field *) (item->real_item()); while ((gr_field= li++)) { - if (field_item->field == gr_field->tmp_field) + if (field_item->field == gr_field->field) return gr_field; } Item_equal *item_equal= item->get_item_equal(); @@ -7646,7 +7508,7 @@ Grouping_tmp_field *find_matching_grouping_field(Item *item, li.rewind(); while ((gr_field= li++)) { - if (field_item->field == gr_field->tmp_field) + if (field_item->field == gr_field->field) return gr_field; } } @@ -7655,26 +7517,25 @@ Grouping_tmp_field *find_matching_grouping_field(Item *item, } -Item *Item_field::derived_grouping_field_transformer_for_where(THD *thd, - uchar *arg) +Item *Item_field::grouping_field_transformer_for_where(THD *thd, uchar *arg) { st_select_lex *sel= (st_select_lex *)arg; - Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel); + Field_pair *gr_field= find_matching_grouping_field(this, sel); if (gr_field) - return gr_field->producing_item->build_clone(thd); + return gr_field->corresponding_item->build_clone(thd); return this; } Item * -Item_direct_view_ref::derived_grouping_field_transformer_for_where(THD *thd, - uchar *arg) +Item_direct_view_ref::grouping_field_transformer_for_where(THD *thd, + uchar *arg) { if (!item_equal) return this; st_select_lex *sel= (st_select_lex *)arg; - Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel); - return gr_field->producing_item->build_clone(thd); + Field_pair *gr_field= find_matching_grouping_field(this, sel); + return gr_field->corresponding_item->build_clone(thd); } void Item_field::print(String *str, enum_query_type query_type) @@ -7710,7 +7571,7 @@ Item_ref::Item_ref(THD *thd, Name_resolution_context *context_arg, /* This constructor used to create some internals references over fixed items */ - if ((set_properties_only= (ref && *ref && (*ref)->fixed))) + if ((set_properties_only= (ref && *ref && (*ref)->is_fixed()))) set_properties(); } @@ -7759,7 +7620,7 @@ Item_ref::Item_ref(THD *thd, TABLE_LIST *view_arg, Item **item, /* This constructor is used to create some internal references over fixed items */ - if ((set_properties_only= (ref && *ref && (*ref)->fixed))) + if ((set_properties_only= (ref && *ref && (*ref)->is_fixed()))) set_properties(); } @@ -7885,7 +7746,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) goto error; /* Some error occurred (e.g. ambiguous names). */ if (ref != not_found_item) { - DBUG_ASSERT(*ref && (*ref)->fixed); + DBUG_ASSERT(*ref && (*ref)->is_fixed()); prev_subselect_item->used_tables_and_const_cache_join(*ref); break; } @@ -8008,7 +7869,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) goto error; } /* Should be checked in resolve_ref_in_select_and_group(). */ - DBUG_ASSERT(*ref && (*ref)->fixed); + DBUG_ASSERT(*ref && (*ref)->is_fixed()); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, this); /* @@ -8033,13 +7894,13 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) */ if (!((*ref)->type() == REF_ITEM && ((Item_ref *)(*ref))->ref_type() == OUTER_REF) && - (((*ref)->with_sum_func && name.str && - !(current_sel->linkage != GLOBAL_OPTIONS_TYPE && + (((*ref)->with_sum_func() && name.str && + !(current_sel->get_linkage() != GLOBAL_OPTIONS_TYPE && current_sel->having_fix_field)) || - !(*ref)->fixed)) + !(*ref)->is_fixed())) { my_error(ER_ILLEGAL_REFERENCE, MYF(0), - name.str, ((*ref)->with_sum_func? + name.str, ((*ref)->with_sum_func() ? "reference to group function": "forward reference in item list")); goto error; @@ -8065,7 +7926,7 @@ void Item_ref::set_properties() We have to remember if we refer to a sum function, to ensure that split_sum_func() doesn't try to change the reference. */ - with_sum_func= (*ref)->with_sum_func; + copy_with_sum_func(*ref); with_param= (*ref)->with_param; with_window_func= (*ref)->with_window_func; with_field= (*ref)->with_field; @@ -8337,9 +8198,9 @@ bool Item_ref::is_null() } -bool Item_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_ref::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return (null_value=(*ref)->get_date_result(ltime,fuzzydate)); + return (null_value=(*ref)->get_date_result(thd, ltime, fuzzydate)); } @@ -8474,9 +8335,9 @@ bool Item_direct_ref::is_null() } -bool Item_direct_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_direct_ref::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return (null_value=(*ref)->get_date(ltime,fuzzydate)); + return (null_value=(*ref)->get_date(thd, ltime, fuzzydate)); } @@ -8488,10 +8349,10 @@ Item_cache_wrapper::~Item_cache_wrapper() Item_cache_wrapper::Item_cache_wrapper(THD *thd, Item *item_arg): Item_result_field(thd), orig_item(item_arg), expr_cache(NULL), expr_value(NULL) { - DBUG_ASSERT(orig_item->fixed); + DBUG_ASSERT(orig_item->is_fixed()); Type_std_attributes::set(orig_item); maybe_null= orig_item->maybe_null; - with_sum_func= orig_item->with_sum_func; + copy_with_sum_func(orig_item); with_param= orig_item->with_param; with_field= orig_item->with_field; name= item_arg->name; @@ -8550,7 +8411,7 @@ void Item_cache_wrapper::print(String *str, enum_query_type query_type) bool Item_cache_wrapper::fix_fields(THD *thd __attribute__((unused)), Item **it __attribute__((unused))) { - DBUG_ASSERT(orig_item->fixed); + DBUG_ASSERT(orig_item->is_fixed()); DBUG_ASSERT(fixed); return FALSE; } @@ -8853,18 +8714,18 @@ bool Item_cache_wrapper::is_null() Get the date value of the possibly cached item */ -bool Item_cache_wrapper::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_cache_wrapper::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { Item *cached_value; DBUG_ENTER("Item_cache_wrapper::get_date"); if (!expr_cache) - DBUG_RETURN((null_value= orig_item->get_date(ltime, fuzzydate))); + DBUG_RETURN((null_value= orig_item->get_date(thd, ltime, fuzzydate))); if ((cached_value= check_cache())) - DBUG_RETURN((null_value= cached_value->get_date(ltime, fuzzydate))); + DBUG_RETURN((null_value= cached_value->get_date(thd, ltime, fuzzydate))); cache(); - DBUG_RETURN((null_value= expr_value->get_date(ltime, fuzzydate))); + DBUG_RETURN((null_value= expr_value->get_date(thd, ltime, fuzzydate))); } @@ -8880,7 +8741,7 @@ int Item_cache_wrapper::save_in_field(Field *to, bool no_conversions) Item* Item_cache_wrapper::get_tmp_table_item(THD *thd) { - if (!orig_item->with_sum_func && !orig_item->const_item()) + if (!orig_item->with_sum_func() && !orig_item->const_item()) return new (thd->mem_root) Item_temptable_field(thd, result_field); return copy_or_same(thd); } @@ -8910,7 +8771,7 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference) /* view fild reference must be defined */ DBUG_ASSERT(*ref); /* (*ref)->check_cols() will be made in Item_direct_ref::fix_fields */ - if ((*ref)->fixed) + if ((*ref)->is_fixed()) { Item *ref_item= (*ref)->real_item(); if (ref_item->type() == Item::FIELD_ITEM) @@ -9196,7 +9057,6 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) if (arg->fix_fields_if_needed(thd, &arg)) goto error; - real_arg= arg->real_item(); if (real_arg->type() != FIELD_ITEM) { @@ -9282,10 +9142,10 @@ my_decimal *Item_default_value::val_decimal(my_decimal *decimal_value) return Item_field::val_decimal(decimal_value); } -bool Item_default_value::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_default_value::get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate) { calculate(); - return Item_field::get_date(ltime, fuzzydate); + return Item_field::get_date(thd, ltime, fuzzydate); } bool Item_default_value::send(Protocol *protocol, st_value *buffer) @@ -9388,7 +9248,7 @@ my_decimal *Item_ignore_value::val_decimal(my_decimal *decimal_value) return 0; } -bool Item_ignore_value::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_ignore_value::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(0); // never should be called null_value= 1; @@ -9412,7 +9272,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) { DBUG_ASSERT(fixed == 0); /* We should only check that arg is in first table */ - if (!arg->fixed) + if (!arg->is_fixed()) { bool res; TABLE_LIST *orig_next_table= context->last_name_resolution_table; @@ -9624,7 +9484,7 @@ void Item_trigger_field::cleanup() Since special nature of Item_trigger_field we should not do most of things from Item_field::cleanup() or Item_ident::cleanup() here. */ - Item::cleanup(); + Item_fixed_hybrid::cleanup(); } @@ -9683,73 +9543,14 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) { - Item_result res_type=item_cmp_type(field->result_type(), - item->result_type()); - /* - We have to check field->cmp_type() instead of res_type, - as result_type() - and thus res_type - can never be TIME_RESULT (yet). - */ - if (field->cmp_type() == TIME_RESULT) - { - MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time; - if (field->type() == MYSQL_TYPE_TIME) - { - field->get_time(&field_time); - item->get_time(&item_time); - } - else - { - field->get_date(&field_time, TIME_INVALID_DATES); - item->get_date(&item_time, TIME_INVALID_DATES); - if (item_time.time_type == MYSQL_TIMESTAMP_TIME) - if (time_to_datetime(thd, &item_time, item_time_cmp= &item_time2)) - return 1; - } - return my_time_compare(&field_time, item_time_cmp); - } - if (res_type == STRING_RESULT) - { - char item_buff[MAX_FIELD_WIDTH]; - char field_buff[MAX_FIELD_WIDTH]; - - String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin); - String field_tmp(field_buff,sizeof(field_buff),&my_charset_bin); - String *item_result= item->val_str(&item_tmp); - /* - Some implementations of Item::val_str(String*) actually modify - the field Item::null_value, hence we can't check it earlier. - */ - if (item->null_value) - return 0; - String *field_result= field->val_str(&field_tmp); - return sortcmp(field_result, item_result, field->charset()); - } - if (res_type == INT_RESULT) - return 0; // Both are of type int - if (res_type == DECIMAL_RESULT) + Type_handler_hybrid_field_type cmp(field->type_handler_for_comparison()); + if (cmp.aggregate_for_comparison(item->type_handler_for_comparison())) { - my_decimal item_buf, *item_val, - field_buf, *field_val; - item_val= item->val_decimal(&item_buf); - if (item->null_value) - return 0; - field_val= field->val_decimal(&field_buf); - return my_decimal_cmp(field_val, item_val); - } - /* - The patch for Bug#13463415 started using this function for comparing - BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode. - Prefixing the auto variables with volatile fixes the problem.... - */ - volatile double result= item->val_real(); - if (item->null_value) + // At fix_fields() time we checked that "field" and "item" are comparable + DBUG_ASSERT(0); return 0; - volatile double field_result= field->val_real(); - if (field_result < result) - return -1; - else if (field_result > result) - return 1; - return 0; + } + return cmp.type_handler()->stored_field_cmp_to_item(thd, field, item); } @@ -9812,7 +9613,6 @@ bool Item_cache_int::cache_value() String *Item_cache_int::val_str(String *str) { - DBUG_ASSERT(fixed == 1); if (!has_value()) return NULL; str->set_int(value, unsigned_flag, default_charset()); @@ -9822,7 +9622,6 @@ String *Item_cache_int::val_str(String *str) my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val) { - DBUG_ASSERT(fixed == 1); if (!has_value()) return NULL; int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val); @@ -9831,7 +9630,6 @@ my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val) double Item_cache_int::val_real() { - DBUG_ASSERT(fixed == 1); if (!has_value()) return 0.0; return (double) value; @@ -9839,7 +9637,6 @@ double Item_cache_int::val_real() longlong Item_cache_int::val_int() { - DBUG_ASSERT(fixed == 1); if (!has_value()) return 0; return value; @@ -9879,88 +9676,12 @@ Item_cache_temporal::Item_cache_temporal(THD *thd, const Type_handler *handler) } -longlong Item_cache_temporal::val_datetime_packed() -{ - DBUG_ASSERT(fixed == 1); - if (Item_cache_temporal::field_type() == MYSQL_TYPE_TIME) - return Item::val_datetime_packed(); // TIME-to-DATETIME conversion needed - if ((!value_cached && !cache_value()) || null_value) - { - null_value= TRUE; - return 0; - } - return value; -} - - -longlong Item_cache_temporal::val_time_packed() -{ - DBUG_ASSERT(fixed == 1); - if (Item_cache_temporal::field_type() != MYSQL_TYPE_TIME) - return Item::val_time_packed(); // DATETIME-to-TIME conversion needed - if ((!value_cached && !cache_value()) || null_value) - { - null_value= TRUE; - return 0; - } - return value; -} - - -String *Item_cache_temporal::val_str(String *str) -{ - DBUG_ASSERT(fixed == 1); - if (!has_value()) - { - null_value= true; - return NULL; - } - return val_string_from_date(str); -} - - -my_decimal *Item_cache_temporal::val_decimal(my_decimal *decimal_value) -{ - DBUG_ASSERT(fixed == 1); - if ((!value_cached && !cache_value()) || null_value) - { - null_value= true; - return NULL; - } - return val_decimal_from_date(decimal_value); -} - - -longlong Item_cache_temporal::val_int() -{ - DBUG_ASSERT(fixed == 1); - if ((!value_cached && !cache_value()) || null_value) - { - null_value= true; - return 0; - } - return val_int_from_date(); -} - - -double Item_cache_temporal::val_real() -{ - DBUG_ASSERT(fixed == 1); - if ((!value_cached && !cache_value()) || null_value) - { - null_value= true; - return 0; - } - return val_real_from_date(); -} - - bool Item_cache_temporal::cache_value() { if (!example) return false; value_cached= true; - value= example->val_datetime_packed_result(); + value= example->val_datetime_packed_result(current_thd); null_value= example->null_value; return true; } @@ -9971,16 +9692,14 @@ bool Item_cache_time::cache_value() if (!example) return false; value_cached= true; - value= example->val_time_packed_result(); + value= example->val_time_packed_result(current_thd); null_value= example->null_value; return true; } -bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_cache_temporal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - ErrConvInteger str(value); - if (!has_value()) { bzero((char*) ltime,sizeof(*ltime)); @@ -9995,7 +9714,7 @@ bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) int Item_cache_temporal::save_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; - if (get_date(<ime, 0)) + if (get_date(field->get_thd(), <ime, date_mode_t(0))) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); int error= field->store_time_dec(<ime, decimals); @@ -10038,21 +9757,21 @@ Item *Item_cache_temporal::convert_to_basic_const_item(THD *thd) Item *Item_cache_datetime::make_literal(THD *thd) { MYSQL_TIME ltime; - unpack_time(val_datetime_packed(), <ime, MYSQL_TIMESTAMP_DATETIME); + unpack_time(val_datetime_packed(thd), <ime, MYSQL_TIMESTAMP_DATETIME); return new (thd->mem_root) Item_datetime_literal(thd, <ime, decimals); } Item *Item_cache_date::make_literal(THD *thd) { MYSQL_TIME ltime; - unpack_time(val_datetime_packed(), <ime, MYSQL_TIMESTAMP_DATE); + unpack_time(val_datetime_packed(thd), <ime, MYSQL_TIMESTAMP_DATE); return new (thd->mem_root) Item_date_literal(thd, <ime); } Item *Item_cache_time::make_literal(THD *thd) { MYSQL_TIME ltime; - unpack_time(val_time_packed(), <ime, MYSQL_TIMESTAMP_TIME); + unpack_time(val_time_packed(thd), <ime, MYSQL_TIMESTAMP_TIME); return new (thd->mem_root) Item_time_literal(thd, <ime, decimals); } @@ -10069,7 +9788,6 @@ bool Item_cache_real::cache_value() double Item_cache_real::val_real() { - DBUG_ASSERT(fixed == 1); if (!has_value()) return 0.0; return value; @@ -10077,7 +9795,6 @@ double Item_cache_real::val_real() longlong Item_cache_real::val_int() { - DBUG_ASSERT(fixed == 1); if (!has_value()) return 0; return Converter_double_to_longlong(value, unsigned_flag).result(); @@ -10086,7 +9803,6 @@ longlong Item_cache_real::val_int() String* Item_cache_real::val_str(String *str) { - DBUG_ASSERT(fixed == 1); if (!has_value()) return NULL; str->set_real(value, decimals, default_charset()); @@ -10096,7 +9812,6 @@ String* Item_cache_real::val_str(String *str) my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val) { - DBUG_ASSERT(fixed == 1); if (!has_value()) return NULL; double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); @@ -10131,38 +9846,22 @@ bool Item_cache_decimal::cache_value() double Item_cache_decimal::val_real() { - DBUG_ASSERT(fixed); - double res; - if (!has_value()) - return 0.0; - my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res); - return res; + return !has_value() ? 0.0 : decimal_value.to_double(); } longlong Item_cache_decimal::val_int() { - DBUG_ASSERT(fixed); - longlong res; - if (!has_value()) - return 0; - my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res); - return res; + return !has_value() ? 0 : decimal_value.to_longlong(unsigned_flag); } String* Item_cache_decimal::val_str(String *str) { - DBUG_ASSERT(fixed); - if (!has_value()) - return NULL; - my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE, - &decimal_value); - my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str); - return str; + return !has_value() ? NULL : + decimal_value.to_string_round(str, decimals, &decimal_value); } my_decimal *Item_cache_decimal::val_decimal(my_decimal *val) { - DBUG_ASSERT(fixed); if (!has_value()) return NULL; return &decimal_value; @@ -10179,9 +9878,8 @@ Item *Item_cache_decimal::convert_to_basic_const_item(THD *thd) new_item= (Item*) new (thd->mem_root) Item_null(thd); else { - my_decimal decimal_value; - my_decimal *result= val_decimal(&decimal_value); - new_item= (Item*) new (thd->mem_root) Item_decimal(thd, result); + VDec tmp(this); + new_item= (Item*) new (thd->mem_root) Item_decimal(thd, tmp.ptr()); } return new_item; } @@ -10214,7 +9912,6 @@ bool Item_cache_str::cache_value() double Item_cache_str::val_real() { - DBUG_ASSERT(fixed == 1); if (!has_value()) return 0.0; return value ? double_from_string_with_check(value) : 0.0; @@ -10223,7 +9920,6 @@ double Item_cache_str::val_real() longlong Item_cache_str::val_int() { - DBUG_ASSERT(fixed == 1); if (!has_value()) return 0; return value ? longlong_from_string_with_check(value) : 0; @@ -10232,7 +9928,6 @@ longlong Item_cache_str::val_int() String* Item_cache_str::val_str(String *str) { - DBUG_ASSERT(fixed == 1); if (!has_value()) return 0; return value; @@ -10241,7 +9936,6 @@ String* Item_cache_str::val_str(String *str) my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) { - DBUG_ASSERT(fixed == 1); if (!has_value()) return NULL; return value ? decimal_from_string_with_check(decimal_val, value) : 0; @@ -10425,7 +10119,7 @@ String *Item_type_holder::val_str(String*) return 0; } -bool Item_type_holder::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_type_holder::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(0); // should never be called return true; @@ -10434,7 +10128,7 @@ bool Item_type_holder::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) void Item_result_field::cleanup() { DBUG_ENTER("Item_result_field::cleanup()"); - Item::cleanup(); + Item_fixed_hybrid::cleanup(); result_field= 0; DBUG_VOID_RETURN; } diff --git a/sql/item.h b/sql/item.h index d743cf6c19c..d15dbade60f 100644 --- a/sql/item.h +++ b/sql/item.h @@ -100,7 +100,10 @@ class sp_head; class Protocol; struct TABLE_LIST; void item_init(void); /* Init item functions */ +class Item_basic_value; +class Item_result_field; class Item_field; +class Item_ref; class Item_param; class user_var_entry; class JOIN; @@ -108,6 +111,7 @@ struct KEY_FIELD; struct SARGABLE_PARAM; class RANGE_OPT_PARAM; class SEL_TREE; +class With_sum_func_cache; enum precedence { LOWEST_PRECEDENCE, @@ -596,6 +600,7 @@ typedef bool (Item::*Item_processor) (void *arg); typedef bool (Item::*Item_analyzer) (uchar **argp); typedef Item* (Item::*Item_transformer) (THD *thd, uchar *arg); typedef void (*Cond_traverser) (const Item *item, void *arg); +typedef bool (Item::*Pushdown_checker) (uchar *arg); struct st_cond_statistic; @@ -628,6 +633,85 @@ public: String_copier_for_item(THD *thd): m_thd(thd) { } }; + +/** + A helper class describing what kind of Item created a temporary field. + - If m_field is set, then the temporary field was created from Field + (e.g. when the Item was Item_field, or Item_ref pointing to Item_field) + - If m_default_field is set, then there is a usable DEFAULT value. + (e.g. when the Item is Item_field) + - If m_item_result_field is set, then the temporary field was created + from certain sub-types of Item_result_field (e.g. Item_func) + See create_tmp_field() in sql_select.cc for details. +*/ + +class Tmp_field_src +{ + Field *m_field; + Field *m_default_field; + Item_result_field *m_item_result_field; +public: + Tmp_field_src() + :m_field(0), + m_default_field(0), + m_item_result_field(0) + { } + Field *field() const { return m_field; } + Field *default_field() const { return m_default_field; } + Item_result_field *item_result_field() const { return m_item_result_field; } + void set_field(Field *field) { m_field= field; } + void set_default_field(Field *field) { m_default_field= field; } + void set_item_result_field(Item_result_field *item) + { m_item_result_field= item; } +}; + + +/** + Parameters for create_tmp_field_ex(). + See create_tmp_field() in sql_select.cc for details. +*/ + +class Tmp_field_param +{ + bool m_group; + bool m_modify_item; + bool m_table_cant_handle_bit_fields; + bool m_make_copy_field; +public: + Tmp_field_param(bool group, + bool modify_item, + bool table_cant_handle_bit_fields, + bool make_copy_field) + :m_group(group), + m_modify_item(modify_item), + m_table_cant_handle_bit_fields(table_cant_handle_bit_fields), + m_make_copy_field(make_copy_field) + { } + bool group() const { return m_group; } + bool modify_item() const { return m_modify_item; } + bool table_cant_handle_bit_fields() const + { return m_table_cant_handle_bit_fields; } + bool make_copy_field() const { return m_make_copy_field; } + void set_modify_item(bool to) { m_modify_item= to; } +}; + + +class Item_const +{ +public: + virtual ~Item_const() {} + virtual const Type_all_attributes *get_type_all_attributes_from_const() const= 0; + virtual bool const_is_null() const { return false; } + virtual const longlong *const_ptr_longlong() const { return NULL; } + virtual const double *const_ptr_double() const { return NULL; } + virtual const my_decimal *const_ptr_my_decimal() const { return NULL; } + virtual const MYSQL_TIME *const_ptr_mysql_time() const { return NULL; } + virtual const String *const_ptr_string() const { return NULL; } +}; + + +/****************************************************************************/ + class Item: public Value_source, public Type_all_attributes { @@ -652,16 +736,26 @@ public: static void operator delete(void *ptr, MEM_ROOT *mem_root) {} enum Type {FIELD_ITEM= 0, FUNC_ITEM, SUM_FUNC_ITEM, - WINDOW_FUNC_ITEM, STRING_ITEM, - INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM, - COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM, - PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, - FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM, + WINDOW_FUNC_ITEM, + /* + NOT NULL literal-alike constants, which do not change their + value during an SQL statement execution, but can optionally + change their value between statements: + - Item_literal - real NOT NULL constants + - Item_param - can change between statements + - Item_splocal - can change between statements + - Item_user_var_as_out_param - hack + Note, Item_user_var_as_out_param actually abuses the type code. + It should be moved out of the Item tree eventually. + */ + CONST_ITEM, + NULL_ITEM, // Item_null or Item_param bound to NULL + COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM, + PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, + FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM, SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER, - PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM, - XPATH_NODESET, XPATH_NODESET_CMP, - VIEW_FIXER_ITEM, EXPR_CACHE_ITEM, - DATE_ITEM}; + PARAM_ITEM, TRIGGER_FIELD_ITEM, + EXPR_CACHE_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; @@ -703,11 +797,34 @@ protected: */ Field *tmp_table_field_from_field_type(TABLE *table) { + DBUG_ASSERT(is_fixed()); const Type_handler *h= type_handler()->type_handler_for_tmp_table(this); return h->make_and_init_table_field(&name, Record_addr(maybe_null), *this, table); } + /** + Create a temporary field for a simple Item, which does not + need any special action after the field creation: + - is not an Item_field descendant (and not a reference to Item_field) + - is not an Item_result_field descendant + - does not need to copy any DEFAULT value to the result Field + - does not need to set Field::is_created_from_null_item for the result + See create_tmp_field_ex() for details on parameters and return values. + */ + Field *create_tmp_field_ex_simple(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param) + { + DBUG_ASSERT(!param->make_copy_field()); + DBUG_ASSERT(!is_result_field()); + DBUG_ASSERT(type() != NULL_ITEM); + return tmp_table_field_from_field_type(table); + } Field *create_tmp_field_int(TABLE *table, uint convert_int_length); + Field *tmp_table_field_from_field_type_maybe_null(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param, + bool is_explicit_null); void push_note_converted_to_negative_complement(THD *thd); void push_note_converted_to_positive_complement(THD *thd); @@ -715,21 +832,21 @@ protected: /* Helper methods, to get an Item value from another Item */ double val_real_from_item(Item *item) { - DBUG_ASSERT(fixed == 1); + DBUG_ASSERT(is_fixed()); double value= item->val_real(); null_value= item->null_value; return value; } longlong val_int_from_item(Item *item) { - DBUG_ASSERT(fixed == 1); + DBUG_ASSERT(is_fixed()); longlong value= item->val_int(); null_value= item->null_value; return value; } String *val_str_from_item(Item *item, String *str) { - DBUG_ASSERT(fixed == 1); + DBUG_ASSERT(is_fixed()); String *res= item->val_str(str); if (res) res->set_charset(collation.collation); @@ -739,31 +856,33 @@ protected: } my_decimal *val_decimal_from_item(Item *item, my_decimal *decimal_value) { - DBUG_ASSERT(fixed == 1); + DBUG_ASSERT(is_fixed()); my_decimal *value= item->val_decimal(decimal_value); if ((null_value= item->null_value)) value= NULL; return value; } - bool get_date_from_item(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date_from_item(THD *thd, Item *item, + MYSQL_TIME *ltime, date_mode_t fuzzydate) { - bool rc= item->get_date(ltime, fuzzydate); + bool rc= item->get_date(thd, ltime, fuzzydate); null_value= MY_TEST(rc || item->null_value); return rc; } +public: /* This method is used if the item was not null but convertion to TIME/DATE/DATETIME failed. We return a zero date if allowed, otherwise - null. */ - bool make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool make_zero_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); -public: /* Cache val_str() into the own buffer, e.g. to evaluate constant expressions with subqueries in the ORDER/GROUP clauses. */ String *val_str() { return val_str(&str_value); } + virtual Item_func *get_item_func() { return NULL; } const MY_LOCALE *locale_from_val_str(); @@ -783,14 +902,12 @@ public: bool in_rollup; /* If used in GROUP BY list of a query with ROLLUP */ bool null_value; /* if item is null */ - bool with_sum_func; /* True if item contains a sum func */ bool with_param; /* True if contains an SP parameter */ bool with_window_func; /* True if item contains a window func */ /** True if any item except Item_sum contains a field. Set during parsing. */ bool with_field; - bool fixed; /* If item fixed with fix_fields */ bool is_autogenerated_name; /* indicate was name of this Item autogenerated or set by user */ // alloc & destruct is done as start of select on THD::mem_root @@ -820,7 +937,7 @@ public: bool fix_fields_if_needed(THD *thd, Item **ref) { - return fixed ? false : fix_fields(thd, ref); + return is_fixed() ? false : fix_fields(thd, ref); } bool fix_fields_if_needed_for_scalar(THD *thd, Item **ref) { @@ -834,7 +951,27 @@ public: { return fix_fields_if_needed_for_scalar(thd, ref); } - virtual bool fix_fields(THD *, Item **); + /* + By default we assume that an Item is fixed by the contstructor. + */ + virtual bool fix_fields(THD *, Item **) + { + /* + This should not normally be called, because usually before + fix_fields() we check is_fixed() to be false. + But historically we allow fix_fields() to be called for Items + who return basic_const_item()==true. + */ + DBUG_ASSERT(is_fixed()); + DBUG_ASSERT(basic_const_item()); + return false; + } + virtual bool is_fixed() const { return true; } + virtual void unfix_fields() + { + DBUG_ASSERT(0); + } + /* Fix after some tables has been pulled out. Basically re-calculate all attributes that are dependent on the tables. @@ -854,11 +991,14 @@ public: but rather uses intermediate type conversion items. Then the method is supposed to be applied recursively. */ - virtual inline void quick_fix_field() { fixed= 1; } + virtual void quick_fix_field() + { + DBUG_ASSERT(0); + } - bool save_in_value(struct st_value *value) + bool save_in_value(THD *thd, struct st_value *value) { - return type_handler()->Item_save_in_value(this, value); + return type_handler()->Item_save_in_value(thd, this, value); } /* Function returns 1 on overflow and -1 on fatal errors */ @@ -883,6 +1023,21 @@ public: return type_handler()->field_type(); } virtual const Type_handler *type_handler() const= 0; + /** + Detects if an Item has a fixed data type which is known + even before fix_fields(). + Currently it's important only to find Items with a fixed boolean + data type. More item types can be marked in the future as having + a fixed data type (e.g. all literals, all fixed type functions, etc). + + @retval NULL if the Item type is not known before fix_fields() + @retval the pointer to the data type handler, if the data type + is known before fix_fields(). + */ + virtual const Type_handler *fixed_type_handler() const + { + return NULL; + } const Type_handler *type_handler_for_comparison() const { return type_handler()->type_handler_for_comparison(); @@ -891,13 +1046,9 @@ public: { return type_handler(); } - virtual const Type_handler *cast_to_int_type_handler() const - { - return type_handler(); - } - virtual const Type_handler *type_handler_for_system_time() const + const Type_handler *cast_to_int_type_handler() const { - return real_type_handler(); + return real_type_handler()->cast_to_int_type_handler(); } /* result_type() of an item specifies how the value should be returned */ Item_result result_type() const @@ -953,6 +1104,10 @@ public: return type_handler()->Item_get_cache(thd, this); } virtual enum Type type() const =0; + bool is_of_type(Type t, Item_result cmp) const + { + return type() == t && cmp_type() == cmp; + } /* real_type() is the type of base item. This is same as type() for most items, except Item_ref() and Item_cache_wrapper() where it @@ -1028,6 +1183,16 @@ public: If value is not null null_value flag will be reset to FALSE. */ virtual longlong val_int()=0; + Longlong_null to_longlong_null() + { + longlong nr= val_int(); + /* + C++ does not guarantee the order of parameter evaluation, + so to make sure "null_value" is passed to the constructor + after the val_int() call, val_int() is caled on a separate line. + */ + return Longlong_null(nr, null_value); + } /** Get a value for CAST(x AS SIGNED). Too large positive unsigned integer values are converted @@ -1049,7 +1214,6 @@ public: { return cast_to_int_type_handler()->Item_val_int_unsigned_typecast(this); } - longlong val_int_unsigned_typecast_from_decimal(); longlong val_int_unsigned_typecast_from_int(); longlong val_int_unsigned_typecast_from_str(); /* @@ -1215,7 +1379,7 @@ public: { return type_handler()->Item_val_bool(this); } - virtual String *val_nodeset(String*) { return 0; } + virtual String *val_raw(String*) { return 0; } /* save_val() is method of val_* family which stores value in the given @@ -1230,28 +1394,15 @@ public: /* Helper functions, see item_sum.cc */ String *val_string_from_real(String *str); String *val_string_from_int(String *str); - String *val_string_from_decimal(String *str); - String *val_string_from_date(String *str); my_decimal *val_decimal_from_real(my_decimal *decimal_value); my_decimal *val_decimal_from_int(my_decimal *decimal_value); my_decimal *val_decimal_from_string(my_decimal *decimal_value); - my_decimal *val_decimal_from_date(my_decimal *decimal_value); - my_decimal *val_decimal_from_time(my_decimal *decimal_value); - longlong val_int_from_decimal(); - longlong val_int_from_date(); longlong val_int_from_real() { - DBUG_ASSERT(fixed == 1); + DBUG_ASSERT(is_fixed()); return Converter_double_to_longlong_with_warn(val_real(), false).result(); } longlong val_int_from_str(int *error); - double val_real_from_decimal(); - double val_real_from_date(); - - // Get TIME, DATE or DATETIME using proper sql_mode flags for the field type - bool get_temporal_with_sql_mode(MYSQL_TIME *ltime); - // Check NULL value for a TIME, DATE or DATETIME expression - bool is_null_from_temporal(); int save_time_in_field(Field *field, bool no_conversions); int save_date_in_field(Field *field, bool no_conversions); @@ -1311,6 +1462,14 @@ public: a constant expression. Used in the optimizer to propagate basic constants. */ virtual bool basic_const_item() const { return 0; } + /* + Test if "this" is an ORDER position (rather than an expression). + Notes: + - can be called before fix_fields(). + - local SP variables (even of integer types) are always expressions, not + positions. (And they can't be used before fix_fields is called for them). + */ + virtual bool is_order_clause_position() const { return false; } /* cloning of constant items (0 if it is not const) */ virtual Item *clone_item(THD *thd) { return 0; } virtual Item* build_clone(THD *thd) { return get_copy(thd); } @@ -1358,14 +1517,14 @@ public: /** TIME or DATETIME precision of the item: 0..6 */ - uint time_precision() + uint time_precision(THD *thd) { - return const_item() ? type_handler()->Item_time_precision(this) : + return const_item() ? type_handler()->Item_time_precision(thd, this) : MY_MIN(decimals, TIME_SECOND_PART_DIGITS); } - uint datetime_precision() + uint datetime_precision(THD *thd) { - return const_item() ? type_handler()->Item_datetime_precision(this) : + return const_item() ? type_handler()->Item_datetime_precision(thd, this) : MY_MIN(decimals, TIME_SECOND_PART_DIGITS); } virtual longlong val_int_min() const @@ -1463,78 +1622,33 @@ public: void split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &fields, Item **ref, uint flags); - virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)= 0; - bool get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_year(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_decimal(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_time(MYSQL_TIME *ltime) - { return get_date(ltime, Time::flags_for_get_date()); } - /* - Get time with automatic DATE/DATETIME to TIME conversion, - by subtracting CURRENT_DATE. - - Performce a reverse operation to CAST(time AS DATETIME) - Suppose: - - we have a set of items (typically with the native MYSQL_TYPE_TIME type) - whose item->get_date() return TIME1 value, and - - CAST(AS DATETIME) for the same Items return DATETIME1, - after applying time-to-datetime conversion to TIME1. - - then all items (typically of the native MYSQL_TYPE_{DATE|DATETIME} types) - whose get_date() return DATETIME1 must also return TIME1 from - get_time_with_conversion() - - @param thd - the thread, its variables.old_mode is checked - to decide if use simple YYYYMMDD truncation (old mode), - or perform full DATETIME-to-TIME conversion with - CURRENT_DATE subtraction. - @param[out] ltime - store the result here - @param fuzzydate - flags to be used for the get_date() call. - Normally, should include TIME_TIME_ONLY, to let - the called low-level routines, e.g. str_to_date(), - know that we prefer TIME rather that DATE/DATETIME - and do less conversion outside of the low-level - routines. - - @returns true - on error, e.g. get_date() returned NULL value, - or get_date() returned DATETIME/DATE with non-zero - YYYYMMDD part. - @returns false - on success - */ - bool get_time_with_conversion(THD *thd, MYSQL_TIME *ltime, - ulonglong fuzzydate); + virtual bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)= 0; + bool get_date_from_int(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_date_from_real(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_date_from_string(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_time(THD *thd, MYSQL_TIME *ltime) + { return get_date(thd, ltime, Time::flags_for_get_date()); } // Get a DATE or DATETIME value in numeric packed format for comparison - virtual longlong val_datetime_packed() + virtual longlong val_datetime_packed(THD *thd) { - ulonglong fuzzydate= TIME_FUZZY_DATES | TIME_INVALID_DATES; - Datetime dt(current_thd, this, fuzzydate); - return dt.is_valid_datetime() ? pack_time(dt.get_mysql_time()) : 0; + date_mode_t fuzzydate= Datetime::comparison_flags_for_get_date(); + return Datetime(current_thd, this, fuzzydate).to_packed(); } // Get a TIME value in numeric packed format for comparison - virtual longlong val_time_packed() + virtual longlong val_time_packed(THD *thd) { - Time tm(this, Time::comparison_flags_for_get_date()); - return tm.is_valid_time() ? pack_time(tm.get_mysql_time()) : 0; + return Time(thd, this, Time::comparison_flags_for_get_date()).to_packed(); } - longlong val_datetime_packed_result(); - longlong val_time_packed_result() + longlong val_datetime_packed_result(THD *thd); + longlong val_time_packed_result(THD *thd) { MYSQL_TIME ltime; - ulonglong fuzzydate= Time::comparison_flags_for_get_date(); - return get_date_result(<ime, fuzzydate) ? 0 : pack_time(<ime); + date_mode_t fuzzydate= Time::comparison_flags_for_get_date(); + return get_date_result(thd, <ime, fuzzydate) ? 0 : pack_time(<ime); } - // Get a temporal value in packed DATE/DATETIME or TIME format - longlong val_temporal_packed(enum_field_types f_type) - { - return f_type == MYSQL_TYPE_TIME ? val_time_packed() : - val_datetime_packed(); - } - bool get_seconds(ulonglong *sec, ulong *sec_part); - virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date(ltime,fuzzydate); } + virtual bool get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date(thd, ltime,fuzzydate); } /* The method allows to determine nullness of a complex expression without fully evaluating it, instead of calling val/result*() then @@ -1549,35 +1663,7 @@ public: */ virtual void update_null_value () { - switch (cmp_type()) { - case INT_RESULT: - (void) val_int(); - break; - case REAL_RESULT: - (void) val_real(); - break; - case DECIMAL_RESULT: - { - my_decimal tmp; - (void) val_decimal(&tmp); - } - break; - case TIME_RESULT: - { - MYSQL_TIME ltime; - (void) get_temporal_with_sql_mode(<ime); - } - break; - case STRING_RESULT: - { - StringBuffer<MAX_FIELD_WIDTH> tmp; - (void) val_str(&tmp); - } - break; - case ROW_RESULT: - DBUG_ASSERT(0); - null_value= true; - } + return type_handler()->Item_update_null_value(this); } /* @@ -1595,10 +1681,9 @@ public: set field of temporary table for Item which can be switched on temporary table during query processing (grouping and so on) */ - virtual void set_result_field(Field *field) {} virtual bool is_result_field() { return 0; } - virtual bool is_bool_type() { return false; } virtual bool is_json_type() { return false; } + virtual bool is_bool_literal() const { return false; } /* This is to handle printing of default values */ virtual bool need_parentheses_in_default() { return false; } virtual void save_in_result_field(bool no_conversions) {} @@ -1709,7 +1794,15 @@ public: or can be converted to such an exression using equalities. Not to be used for AND/OR formulas. */ - virtual bool excl_dep_on_grouping_fields(st_select_lex *sel) { return false; } + virtual bool excl_dep_on_grouping_fields(st_select_lex *sel) + { return false; } + /* + TRUE if the expression depends only on fields from the left part of + IN subquery or can be converted to such an expression using equalities. + Not to be used for AND/OR formulas. + */ + virtual bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) + { return false; } virtual bool switch_to_nullable_fields_processor(void *arg) { return 0; } virtual bool find_function_processor (void *arg) { return 0; } @@ -1869,11 +1962,17 @@ public: return Type_handler::type_handler_long_or_longlong(max_char_length()); } - virtual Field *create_tmp_field(bool group, TABLE *table) - { - return tmp_table_field_from_field_type(table); - } - + /** + Create field for temporary table. + @param table Temporary table + @param [OUT] src Who created the fields + @param param Create parameters + @retval NULL (on error) + @retval a pointer to a newly create Field (on success) + */ + virtual Field *create_tmp_field_ex(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param)= 0; virtual Item_field *field_for_view_update() { return 0; } virtual Item *neg_transformer(THD *thd) { return NULL; } @@ -1885,8 +1984,12 @@ public: { return this; } virtual Item *derived_field_transformer_for_where(THD *thd, uchar *arg) { return this; } - virtual Item *derived_grouping_field_transformer_for_where(THD *thd, - uchar *arg) + virtual Item *grouping_field_transformer_for_where(THD *thd, uchar *arg) + { return this; } + /* Now is not used. */ + virtual Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg) + { return this; } + virtual Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg) { return this; } virtual Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg) { return this; } @@ -1940,6 +2043,7 @@ public: delete this; } + virtual const Item_const *get_item_const() const { return NULL; } virtual Item_splocal *get_item_splocal() { return 0; } virtual Rewritable_query_parameter *get_rewritable_query_parameter() { return 0; } @@ -2013,13 +2117,16 @@ public: /* Return TRUE if the item points to a column of an outer-joined table. */ - virtual bool is_outer_field() const { DBUG_ASSERT(fixed); return FALSE; } + virtual bool is_outer_field() const { DBUG_ASSERT(is_fixed()); return FALSE; } /** Checks if this item or any of its decendents contains a subquery. This is a replacement of the former Item::has_subquery() and Item::with_subselect. */ - virtual bool with_subquery() const { DBUG_ASSERT(fixed); return false; } + virtual bool with_subquery() const { DBUG_ASSERT(is_fixed()); return false; } + + virtual bool with_sum_func() const { return false; } + virtual With_sum_func_cache* get_with_sum_func_cache() { return NULL; } Item* set_expr_cache(THD *thd); @@ -2083,6 +2190,33 @@ public: { marker &= ~EXTRACTION_MASK; } + void check_pushable_cond(Pushdown_checker excl_dep_func, uchar *arg); + bool pushable_cond_checker_for_derived(uchar *arg) + { + return excl_dep_on_table(*((table_map *)arg)); + } + bool pushable_cond_checker_for_subquery(uchar *arg) + { + return excl_dep_on_in_subq_left_part((Item_in_subselect *)arg); + } + Item *get_corresponding_field_in_insubq(Item_in_subselect *subq_pred); + Item *build_pushable_cond(THD *thd, + Pushdown_checker checker, + uchar *arg); + /* + Checks if this item depends only on the arg table + */ + bool pushable_equality_checker_for_derived(uchar *arg) + { + return (used_tables() == *((table_map *)arg)); + } + /* + Checks if this item consists in the left part of arg IN subquery predicate + */ + bool pushable_equality_checker_for_subquery(uchar *arg) + { + return get_corresponding_field_in_insubq((Item_in_subselect *)arg); + } }; MEM_ROOT *get_thd_memroot(THD *thd); @@ -2097,6 +2231,66 @@ inline Item* get_item_copy (THD *thd, T* item) } +#ifndef DBUG_OFF +/** + A helper class to print the data type and the value for an Item + in debug builds. +*/ +class DbugStringItemTypeValue: public StringBuffer<128> +{ +public: + DbugStringItemTypeValue(THD *thd, const Item *item) + { + append('('); + append(item->type_handler()->name().ptr()); + append(')'); + const_cast<Item*>(item)->print(this, QT_EXPLAIN); + } +}; +#endif + +class With_sum_func_cache +{ +protected: + bool m_with_sum_func; // True if the owner item contains a sum func +public: + With_sum_func_cache() + :m_with_sum_func(false) + { } + With_sum_func_cache(const Item *a) + :m_with_sum_func(a->with_sum_func()) + { } + With_sum_func_cache(const Item *a, const Item *b) + :m_with_sum_func(a->with_sum_func() || b->with_sum_func()) + { } + With_sum_func_cache(const Item *a, const Item *b, const Item *c) + :m_with_sum_func(a->with_sum_func() || b->with_sum_func() || + c->with_sum_func()) + { } + With_sum_func_cache(const Item *a, const Item *b, const Item *c, + const Item *d) + :m_with_sum_func(a->with_sum_func() || b->with_sum_func() || + c->with_sum_func() || d->with_sum_func()) + { } + With_sum_func_cache(const Item *a, const Item *b, const Item *c, + const Item *d, const Item *e) + :m_with_sum_func(a->with_sum_func() || b->with_sum_func() || + c->with_sum_func() || d->with_sum_func() || + e->with_sum_func()) + { } + void set_with_sum_func() { m_with_sum_func= true; } + void reset_with_sum_func() { m_with_sum_func= false; } + void copy_with_sum_func(const Item *item) + { + m_with_sum_func= item->with_sum_func(); + } + void join_with_sum_func(const Item *item) + { + m_with_sum_func|= item->with_sum_func(); + } +}; + + /* This class is a replacement for the former member Item::with_subselect. Determines if the descendant Item is a subselect or some of @@ -2220,6 +2414,17 @@ protected: } return true; } + bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) + { + for (uint i= 0; i < arg_count; i++) + { + if (args[i]->const_item()) + continue; + if (!args[i]->excl_dep_on_in_subq_left_part(subq_pred)) + return false; + } + return true; + } public: Item_args(void) :args(NULL), arg_count(0) @@ -2271,6 +2476,30 @@ public: { args[arg_count++]= item; } + /** + Extract row elements from the given position. + For example, for this input: (1,2),(3,4),(5,6) + pos=0 will extract (1,3,5) + pos=1 will extract (2,4,6) + @param thd - current thread, to allocate memory on its mem_root + @param rows - an array of compatible ROW-type items + @param pos - the element position to extract + */ + bool alloc_and_extract_row_elements(THD *thd, const Item_args *rows, uint pos) + { + DBUG_ASSERT(rows->argument_count() > 0); + DBUG_ASSERT(rows->arguments()[0]->cols() > pos); + if (alloc_arguments(thd, rows->argument_count())) + return true; + for (uint i= 0; i < rows->argument_count(); i++) + { + DBUG_ASSERT(rows->arguments()[0]->cols() == rows->arguments()[i]->cols()); + Item *arg= rows->arguments()[i]->element_index(pos); + add_argument(arg); + } + DBUG_ASSERT(argument_count() == rows->argument_count()); + return false; + } inline Item **arguments() const { return args; } inline uint argument_count() const { return arg_count; } inline void remove_arguments() { arg_count=0; } @@ -2305,27 +2534,39 @@ public: class Item_string; -/** - A common class for Item_basic_constant and Item_param -*/ -class Item_basic_value :public Item +class Item_fixed_hybrid: public Item { - bool is_basic_value(const Item *item, Type type_arg) const - { - return item->basic_const_item() && item->type() == type_arg; - } - bool is_basic_value(Type type_arg) const +public: + bool fixed; // If item was fixed with fix_fields +public: + Item_fixed_hybrid(THD *thd): Item(thd), fixed(false) + { } + Item_fixed_hybrid(THD *thd, Item_fixed_hybrid *item) + :Item(thd, item), fixed(item->fixed) + { } + bool fix_fields(THD *thd, Item **ref) { - return basic_const_item() && type() == type_arg; + DBUG_ASSERT(!fixed); + fixed= true; + return false; } - bool str_eq(const String *value, - const String *other, CHARSET_INFO *cs, bool binary_cmp) const + void cleanup() { - return binary_cmp ? - value->bin_eq(other) : - collation.collation == cs && value->eq(other, collation.collation); + Item::cleanup(); + fixed= false; } + void quick_fix_field() { fixed= true; } + void unfix_fields() { fixed= false; } + bool is_fixed() const { return fixed; } +}; + +/** + A common class for Item_basic_constant and Item_param +*/ +class Item_basic_value :public Item, + public Item_const +{ protected: // Value metadata, e.g. to make string processing easier class Metadata: private MY_STRING_METADATA @@ -2362,66 +2603,40 @@ protected: fix_charset_and_length(str.charset(), dv, Metadata(&str)); } Item_basic_value(THD *thd): Item(thd) {} - /* - In the xxx_eq() methods below we need to cast off "const" to - call val_xxx(). This is OK for Item_basic_constant and Item_param. - */ - bool null_eq(const Item *item) const - { - DBUG_ASSERT(is_basic_value(NULL_ITEM)); - return item->type() == NULL_ITEM; - } - bool str_eq(const String *value, const Item *item, bool binary_cmp) const - { - DBUG_ASSERT(is_basic_value(STRING_ITEM)); - return is_basic_value(item, STRING_ITEM) && - str_eq(value, ((Item_basic_value*)item)->val_str(NULL), - item->collation.collation, binary_cmp); - } - bool real_eq(double value, const Item *item) const - { - DBUG_ASSERT(is_basic_value(REAL_ITEM)); - return is_basic_value(item, REAL_ITEM) && - value == ((Item_basic_value*)item)->val_real(); - } - bool int_eq(longlong value, const Item *item) const +public: + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) { - DBUG_ASSERT(is_basic_value(INT_ITEM)); - return is_basic_value(item, INT_ITEM) && - value == ((Item_basic_value*)item)->val_int() && - (value >= 0 || item->unsigned_flag == unsigned_flag); + + /* + create_tmp_field_ex() for this type of Items is called for: + - CREATE TABLE ... SELECT + - In ORDER BY: SELECT max(a) FROM t1 GROUP BY a ORDER BY 'const'; + - In CURSORS: + DECLARE c CURSOR FOR SELECT 'test'; + OPEN c; + */ + return tmp_table_field_from_field_type_maybe_null(table, src, param, + type() == Item::NULL_ITEM); } + bool eq(const Item *item, bool binary_cmp) const; + const Type_all_attributes *get_type_all_attributes_from_const() const + { return this; } }; class Item_basic_constant :public Item_basic_value { - table_map used_table_map; public: - Item_basic_constant(THD *thd): Item_basic_value(thd), used_table_map(0) {}; - void set_used_tables(table_map map) { used_table_map= map; } - table_map used_tables() const { return used_table_map; } - bool check_vcol_func_processor(void *arg) { return FALSE;} + Item_basic_constant(THD *thd): Item_basic_value(thd) {}; + bool check_vcol_func_processor(void *arg) { return false; } + const Item_const *get_item_const() const { return this; } virtual Item_basic_constant *make_string_literal_concat(THD *thd, const LEX_CSTRING *) { DBUG_ASSERT(0); return this; } - /* to prevent drop fixed flag (no need parent cleanup call) */ - void cleanup() - { - /* - Restore the original field name as it might not have been allocated - in the statement memory. If the name is auto generated, it must be - done again between subsequent executions of a prepared statement. - */ - if (orig_name) - { - name.str= orig_name; - name.length= strlen(orig_name); - } - } }; @@ -2432,7 +2647,7 @@ public: - CASE expression (Item_case_expr); *****************************************************************************/ -class Item_sp_variable :public Item +class Item_sp_variable :public Item_fixed_hybrid { protected: /* @@ -2464,7 +2679,7 @@ public: longlong val_int(); String *val_str(String *sp); my_decimal *val_decimal(my_decimal *decimal_value); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool is_null(); public: @@ -2472,6 +2687,11 @@ public: inline bool const_item() const; + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + return create_tmp_field_ex_simple(table, src, param); + } inline int save_in_field(Field *field, bool no_conversions); inline bool send(Protocol *protocol, st_value *buffer); bool check_vcol_func_processor(void *arg) @@ -2575,6 +2795,20 @@ public: */ Field *create_field_for_create_select(TABLE *table) { return create_table_field_from_handler(table); } + + bool is_valid_limit_clause_variable_with_error() const + { + /* + In case if the variable has an anchored data type, e.g.: + DECLARE a TYPE OF t1.a; + type_handler() is set to &type_handler_null and this + function detects such variable as not valid in LIMIT. + */ + if (type_handler()->is_limit_clause_valid_type()) + return true; + my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0)); + return false; + } }; @@ -2721,11 +2955,10 @@ inline enum Item::Type Item_case_expr::type() const extract a common base with class Item_ref, too. */ -class Item_name_const : public Item +class Item_name_const : public Item_fixed_hybrid { Item *value_item; Item *name_item; - bool valid_args; public: Item_name_const(THD *thd, Item *name_arg, Item *val); @@ -2736,7 +2969,7 @@ public: longlong val_int(); String *val_str(String *sp); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool is_null(); virtual void print(String *str, enum_query_type query_type); @@ -2750,6 +2983,17 @@ public: return TRUE; } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + /* + We can get to here when using a CURSOR for a query with NAME_CONST(): + DECLARE c CURSOR FOR SELECT NAME_CONST('x','y') FROM t1; + OPEN c; + */ + return tmp_table_field_from_field_type_maybe_null(table, src, param, + type() == Item::NULL_ITEM); + } int save_in_field(Field *field, bool no_conversions) { return value_item->save_in_field(field, no_conversions); @@ -2767,15 +3011,27 @@ public: { return get_item_copy<Item_name_const>(thd, this); } }; -class Item_num: public Item_basic_constant + +class Item_literal: public Item_basic_constant { public: - Item_num(THD *thd): Item_basic_constant(thd) { collation.set_numeric(); } + Item_literal(THD *thd): Item_basic_constant(thd) + { } + enum Type type() const { return CONST_ITEM; } + bool check_partition_func_processor(void *int_arg) { return false;} + bool const_item() const { return true; } + bool basic_const_item() const { return true; } +}; + + +class Item_num: public Item_literal +{ +public: + Item_num(THD *thd): Item_literal(thd) { collation.set_numeric(); } Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); - bool check_partition_func_processor(void *int_arg) { return FALSE;} - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } }; @@ -2784,24 +3040,26 @@ public: class st_select_lex; -class Item_result_field :public Item /* Item with result field */ +class Item_result_field :public Item_fixed_hybrid /* Item with result field */ { public: Field *result_field; /* Save result here */ - Item_result_field(THD *thd): Item(thd), result_field(0) {} + Item_result_field(THD *thd): Item_fixed_hybrid(thd), result_field(0) {} // Constructor used for Item_sum/Item_cond_and/or (see Item comment) Item_result_field(THD *thd, Item_result_field *item): - Item(thd, item), result_field(item->result_field) + Item_fixed_hybrid(thd, item), result_field(item->result_field) {} ~Item_result_field() {} /* Required with gcc 2.95 */ Field *get_tmp_table_field() { return result_field; } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param); + void get_tmp_field_src(Tmp_field_src *src, const Tmp_field_param *param); /* This implementation of used_tables() used by Item_avg_field and Item_variance_field which work when only temporary table left, so theu return table map of the temporary table. */ table_map used_tables() const { return 1; } - void set_result_field(Field *field) { result_field= field; } bool is_result_field() { return true; } void save_in_result_field(bool no_conversions) { @@ -2894,11 +3152,17 @@ public: Type_std_attributes::set(par_field->type_std_attributes()); } enum Type type() const { return FIELD_ITEM; } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + DBUG_ASSERT(0); + return 0; + } double val_real() { return field->val_real(); } longlong val_int() { return field->val_int(); } String *val_str(String *str) { return field->val_str(str); } my_decimal *val_decimal(my_decimal *dec) { return field->val_decimal(dec); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { return field->get_date(ltime, fuzzydate); } @@ -2999,24 +3263,25 @@ public: const Type_handler *handler= field->type_handler(); return handler->type_handler_for_item_field(); } - const Type_handler *cast_to_int_type_handler() const - { - return field->type_handler()->cast_to_int_type_handler(); - } const Type_handler *real_type_handler() const { if (field->is_created_from_null_item) return &type_handler_null; return field->type_handler(); } + Field *create_tmp_field_from_item_field(TABLE *new_table, + Item_ref *orig_item, + const Tmp_field_param *param); + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param); TYPELIB *get_typelib() const { return field->get_typelib(); } enum_monotonicity_info get_monotonicity_info() const { return MONOTONIC_STRICT_INCREASING; } longlong val_int_endpoint(bool left_endp, bool *incl_endp); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_result(MYSQL_TIME *ltime,ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_date_result(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate); bool is_null() { return field->is_null(); } void update_null_value(); void update_table_bitmaps() @@ -3026,13 +3291,7 @@ public: TABLE *tab= field->table; tab->covering_keys.intersect(field->part_of_key); if (tab->read_set) - bitmap_fast_test_and_set(tab->read_set, field->field_index); - /* - Do not mark a self-referecing virtual column. - Such virtual columns are reported as invalid. - */ - if (field->vcol_info && tab->vcol_set) - tab->mark_virtual_col(field); + tab->mark_column_with_deps(field); } } void update_used_tables() @@ -3109,10 +3368,13 @@ public: virtual Item *update_value_transformer(THD *thd, uchar *select_arg); Item *derived_field_transformer_for_having(THD *thd, uchar *arg); Item *derived_field_transformer_for_where(THD *thd, uchar *arg); - Item *derived_grouping_field_transformer_for_where(THD *thd, uchar *arg); + Item *grouping_field_transformer_for_where(THD *thd, uchar *arg); + Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg); + Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg); virtual void print(String *str, enum_query_type query_type); bool excl_dep_on_table(table_map tab_map); bool excl_dep_on_grouping_fields(st_select_lex *sel); + bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred); bool cleanup_excluding_fields_processor(void *arg) { return field ? 0 : cleanup_processor(arg); } bool cleanup_excluding_const_fields_processor(void *arg) @@ -3217,22 +3479,21 @@ public: max_length= 0; name.str= name_par ? name_par : "NULL"; name.length= strlen(name.str); - fixed= 1; collation.set(cs, DERIVATION_IGNORABLE, MY_REPERTOIRE_ASCII); } enum Type type() const { return NULL_ITEM; } - bool eq(const Item *item, bool binary_cmp) const { return null_eq(item); } double val_real(); longlong val_int(); String *val_str(String *str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); int save_in_field(Field *field, bool no_conversions); int save_safe_in_field(Field *field); bool send(Protocol *protocol, st_value *buffer); const Type_handler *type_handler() const { return &type_handler_null; } bool basic_const_item() const { return 1; } Item *clone_item(THD *thd); + bool const_is_null() const { return true; } bool is_null() { return 1; } virtual inline void print(String *str, enum_query_type query_type) @@ -3258,6 +3519,12 @@ public: { return result_field->type(); } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + DBUG_ASSERT(0); + return NULL; + } void save_in_result_field(bool no_conversions) { save_in_field(result_field, no_conversions); @@ -3325,8 +3592,8 @@ class Item_param :public Item_basic_value, All Item_param::set_xxx() make sure to do so. In the state with an assigned value: - Item_param::basic_const_item() returns true - - Item::type() returns NULL_ITEM, INT_ITEM, REAL_ITEM, DECIMAL_ITEM, - DATE_ITEM, STRING_ITEM, depending on the value assigned. + - Item::type() returns NULL_ITEM or CONST_ITEM, + depending on the value assigned. So in this state Item_param behaves in many cases like a literal. When Item_param::cleanup() is called: @@ -3349,14 +3616,6 @@ class Item_param :public Item_basic_value, DEFAULT_VALUE, IGNORE_VALUE } state; - enum Type item_type; - - void fix_type(Type type) - { - item_type= type; - fixed= true; - } - void fix_temporal(uint32 max_length_arg, uint decimals_arg); struct CONVERSION_INFO @@ -3455,7 +3714,6 @@ class Item_param :public Item_basic_value, PValue value; const String *value_query_val_str(THD *thd, String* str) const; - bool value_eq(const Item *item, bool binary_cmp) const; Item *value_clone_item(THD *thd); bool can_return_value() const; @@ -3479,9 +3737,57 @@ public: enum Type type() const { - DBUG_ASSERT(fixed || state == NO_VALUE); - return item_type; + // Don't pretend to be a constant unless value for this item is set. + switch (state) { + case NO_VALUE: return PARAM_ITEM; + case NULL_VALUE: return NULL_ITEM; + case SHORT_DATA_VALUE: return CONST_ITEM; + case LONG_DATA_VALUE: return CONST_ITEM; + case DEFAULT_VALUE: return PARAM_ITEM; + case IGNORE_VALUE: return PARAM_ITEM; + } + DBUG_ASSERT(0); + return PARAM_ITEM; + } + + bool is_order_clause_position() const + { + return state == SHORT_DATA_VALUE && + type_handler()->is_order_clause_position_type(); + } + + const Item_const *get_item_const() const + { + switch (state) { + case SHORT_DATA_VALUE: + case LONG_DATA_VALUE: + case NULL_VALUE: + return this; + case IGNORE_VALUE: + case DEFAULT_VALUE: + case NO_VALUE: + break; + } + return NULL; + } + + bool const_is_null() const { return state == NULL_VALUE; } + bool can_return_const_value(Item_result type) const + { + return can_return_value() && + value.type_handler()->cmp_type() == type && + type_handler()->cmp_type() == type; } + const longlong *const_ptr_longlong() const + { return can_return_const_value(INT_RESULT) ? &value.integer : NULL; } + const double *const_ptr_double() const + { return can_return_const_value(REAL_RESULT) ? &value.real : NULL; } + const my_decimal *const_ptr_my_decimal() const + { return can_return_const_value(DECIMAL_RESULT) ? &value.m_decimal : NULL; } + const MYSQL_TIME *const_ptr_mysql_time() const + { return can_return_const_value(TIME_RESULT) ? &value.time : NULL; } + const String *const_ptr_string() const + { return can_return_const_value(STRING_RESULT) ? &value.m_string : NULL; } double val_real() { @@ -3499,7 +3805,7 @@ public: { return can_return_value() ? value.val_str(str, this) : NULL; } - bool get_date(MYSQL_TIME *tm, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *tm, date_mode_t fuzzydate); int save_in_field(Field *field, bool no_conversions); void set_default(); @@ -3576,8 +3882,14 @@ public: so no one will use parameters value in fix_fields still parameter is constant during execution. */ + bool const_item() const + { + return state != NO_VALUE; + } virtual table_map used_tables() const - { return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; } + { + return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; + } virtual void print(String *str, enum_query_type query_type); bool is_null() { DBUG_ASSERT(state != NO_VALUE); return state == NULL_VALUE; } @@ -3607,12 +3919,6 @@ public: */ Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); Item *clone_item(THD *thd); - /* - Implement by-value equality evaluation if parameter value - is set and is a basic constant (integer, real or string). - Otherwise return FALSE. - */ - bool eq(const Item *item, bool binary_cmp) const; void set_param_type_and_swap_value(Item_param *from); Rewritable_query_parameter *get_rewritable_query_parameter() @@ -3660,50 +3966,44 @@ public: longlong value; Item_int(THD *thd, int32 i,size_t length= MY_INT32_NUM_DECIMAL_DIGITS): Item_num(thd), value((longlong) i) - { max_length=(uint32)length; fixed= 1; } + { max_length=(uint32)length; } Item_int(THD *thd, longlong i,size_t length= MY_INT64_NUM_DECIMAL_DIGITS): Item_num(thd), value(i) - { max_length=(uint32)length; fixed= 1; } + { max_length=(uint32)length; } Item_int(THD *thd, ulonglong i, size_t length= MY_INT64_NUM_DECIMAL_DIGITS): Item_num(thd), value((longlong)i) - { max_length=(uint32)length; fixed= 1; unsigned_flag= 1; } + { max_length=(uint32)length; unsigned_flag= 1; } Item_int(THD *thd, const char *str_arg,longlong i,size_t length): Item_num(thd), value(i) { max_length=(uint32)length; name.str= str_arg; name.length= safe_strlen(name.str); - fixed= 1; } Item_int(THD *thd, const char *str_arg,longlong i,size_t length, bool flag): Item_num(thd), value(i) { max_length=(uint32)length; name.str= str_arg; name.length= safe_strlen(name.str); - fixed= 1; unsigned_flag= flag; } Item_int(THD *thd, const char *str_arg, size_t length=64); - enum Type type() const { return INT_ITEM; } const Type_handler *type_handler() const { return type_handler_long_or_longlong(); } - Field *create_tmp_field(bool group, TABLE *table) - { return tmp_table_field_from_field_type(table); } Field *create_field_for_create_select(TABLE *table) { return tmp_table_field_from_field_type(table); } - longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } - longlong val_int_min() const { DBUG_ASSERT(fixed == 1); return value; } - double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; } + const longlong *const_ptr_longlong() const { return &value; } + longlong val_int() { return value; } + longlong val_int_min() const { return value; } + double val_real() { return (double) value; } my_decimal *val_decimal(my_decimal *); String *val_str(String*); int save_in_field(Field *field, bool no_conversions); - bool basic_const_item() const { return 1; } + bool is_order_clause_position() const { return true; } Item *clone_item(THD *thd); virtual void print(String *str, enum_query_type query_type); Item *neg(THD *thd); uint decimal_precision() const { return (uint) (max_length - MY_TEST(value < 0)); } - bool eq(const Item *item, bool binary_cmp) const - { return int_eq(value, item); } Item *get_copy(THD *thd) { return get_item_copy<Item_int>(thd, this); } }; @@ -3719,8 +4019,20 @@ class Item_bool :public Item_int public: Item_bool(THD *thd, const char *str_arg, longlong i): Item_int(thd, str_arg, i, 1) {} - bool is_bool_type() { return true; } + Item_bool(THD *thd, bool i) :Item_int(thd, (longlong) i, 1) { } + bool is_bool_literal() const { return true; } Item *neg_transformer(THD *thd); + const Type_handler *type_handler() const + { return &type_handler_bool; } + const Type_handler *fixed_type_handler() const + { return &type_handler_bool; } + void quick_fix_field() + { + /* + We can get here when Item_bool is created instead of a constant + predicate at various condition optimization stages in sql_select. + */ + } }; @@ -3730,8 +4042,7 @@ public: Item_uint(THD *thd, const char *str_arg, size_t length); Item_uint(THD *thd, ulonglong i): Item_int(thd, i, 10) {} Item_uint(THD *thd, const char *str_arg, longlong i, uint length); - double val_real() - { DBUG_ASSERT(fixed == 1); return ulonglong2double((ulonglong)value); } + double val_real() { return ulonglong2double((ulonglong)value); } String *val_str(String*); Item *clone_item(THD *thd); virtual void print(String *str, enum_query_type query_type); @@ -3752,7 +4063,7 @@ public: longlong val_int(); double val_real() { return (double)val_int(); } void set(longlong packed, enum_mysql_timestamp_type ts_type); - bool get_date(MYSQL_TIME *to, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) { *to= ltime; return false; @@ -3770,24 +4081,26 @@ public: CHARSET_INFO *charset); Item_decimal(THD *thd, const char *str, const my_decimal *val_arg, uint decimal_par, uint length); - Item_decimal(THD *thd, my_decimal *value_par); + Item_decimal(THD *thd, const my_decimal *value_par); Item_decimal(THD *thd, longlong val, bool unsig); Item_decimal(THD *thd, double val, int precision, int scale); Item_decimal(THD *thd, const uchar *bin, int precision, int scale); - enum Type type() const { return DECIMAL_ITEM; } const Type_handler *type_handler() const { return &type_handler_newdecimal; } - longlong val_int(); - double val_real(); - String *val_str(String*); + longlong val_int() { return decimal_value.to_longlong(unsigned_flag); } + double val_real() { return decimal_value.to_double(); } + String *val_str(String *to) { return decimal_value.to_string(to); } my_decimal *val_decimal(my_decimal *val) { return &decimal_value; } + const my_decimal *const_ptr_my_decimal() const { return &decimal_value; } int save_in_field(Field *field, bool no_conversions); - bool basic_const_item() const { return 1; } Item *clone_item(THD *thd); - virtual void print(String *str, enum_query_type query_type); + virtual void print(String *str, enum_query_type query_type) + { + decimal_value.to_string(&str_value); + str->append(str_value); + } Item *neg(THD *thd); uint decimal_precision() const { return decimal_value.precision(); } - bool eq(const Item *, bool binary_cmp) const; void set_decimal_value(my_decimal *value_par); Item *get_copy(THD *thd) { return get_item_copy<Item_decimal>(thd, this); } @@ -3807,21 +4120,18 @@ public: name.length= safe_strlen(str); decimals=(uint8) decimal_par; max_length= length; - fixed= 1; } Item_float(THD *thd, double value_par, uint decimal_par): Item_num(thd), presentation(0), value(value_par) { decimals= (uint8) decimal_par; - fixed= 1; } int save_in_field(Field *field, bool no_conversions); - enum Type type() const { return REAL_ITEM; } const Type_handler *type_handler() const { return &type_handler_double; } - double val_real() { DBUG_ASSERT(fixed == 1); return value; } + const double *const_ptr_double() const { return &value; } + double val_real() { return value; } longlong val_int() { - DBUG_ASSERT(fixed == 1); if (value <= (double) LONGLONG_MIN) { return LONGLONG_MIN; @@ -3834,12 +4144,9 @@ public: } String *val_str(String*); my_decimal *val_decimal(my_decimal *); - bool basic_const_item() const { return 1; } Item *clone_item(THD *thd); Item *neg(THD *thd); virtual void print(String *str, enum_query_type query_type); - bool eq(const Item *item, bool binary_cmp) const - { return real_eq(value, item); } Item *get_copy(THD *thd) { return get_item_copy<Item_float>(thd, this); } }; @@ -3866,14 +4173,12 @@ public: }; -class Item_string :public Item_basic_constant +class Item_string :public Item_literal { protected: void fix_from_value(Derivation dv, const Metadata metadata) { fix_charset_and_length(str_value.charset(), dv, metadata); - // it is constant => can be used without fix_fields (and frequently used) - fixed= 1; } void fix_and_set_name_from_value(THD *thd, Derivation dv, const Metadata metadata) @@ -3884,41 +4189,41 @@ protected: protected: /* Just create an item and do not fill string representation */ Item_string(THD *thd, CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE): - Item_basic_constant(thd) + Item_literal(thd) { collation.set(cs, dv); max_length= 0; set_name(thd, NULL, 0, system_charset_info); decimals= NOT_FIXED_DEC; - fixed= 1; } public: - Item_string(THD *thd, CHARSET_INFO *csi, const char *str_arg, uint length_arg): - Item_basic_constant(thd) + Item_string(THD *thd, CHARSET_INFO *csi, const char *str_arg, uint length_arg) + :Item_literal(thd) { collation.set(csi, DERIVATION_COERCIBLE); set_name(thd, NULL, 0, system_charset_info); decimals= NOT_FIXED_DEC; - fixed= 1; str_value.copy(str_arg, length_arg, csi); max_length= str_value.numchars() * csi->mbmaxlen; } // Constructors with the item name set from its value Item_string(THD *thd, const char *str, uint length, CHARSET_INFO *cs, - Derivation dv, uint repertoire): Item_basic_constant(thd) + Derivation dv, uint repertoire) + :Item_literal(thd) { str_value.set_or_copy_aligned(str, length, cs); fix_and_set_name_from_value(thd, dv, Metadata(&str_value, repertoire)); } Item_string(THD *thd, const char *str, size_t length, - CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE): - Item_basic_constant(thd) + CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) + :Item_literal(thd) { str_value.set_or_copy_aligned(str, length, cs); fix_and_set_name_from_value(thd, dv, Metadata(&str_value)); } Item_string(THD *thd, const String *str, CHARSET_INFO *tocs, uint *conv_errors, - Derivation dv, uint repertoire): Item_basic_constant(thd) + Derivation dv, uint repertoire) + :Item_literal(thd) { if (str_value.copy(str, tocs, conv_errors)) str_value.set("", 0, tocs); // EOM ? @@ -3927,16 +4232,16 @@ public: } // Constructors with an externally provided item name Item_string(THD *thd, const char *name_par, const char *str, size_t length, - CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE): - Item_basic_constant(thd) + CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) + :Item_literal(thd) { str_value.set_or_copy_aligned(str, length, cs); fix_from_value(dv, Metadata(&str_value)); set_name(thd, name_par,safe_strlen(name_par), system_charset_info); } Item_string(THD *thd, const char *name_par, const char *str, size_t length, - CHARSET_INFO *cs, Derivation dv, uint repertoire): - Item_basic_constant(thd) + CHARSET_INFO *cs, Derivation dv, uint repertoire) + :Item_literal(thd) { str_value.set_or_copy_aligned(str, length, cs); fix_from_value(dv, Metadata(&str_value, repertoire)); @@ -3946,26 +4251,23 @@ public: { str_value.print(to); } - enum Type type() const { return STRING_ITEM; } double val_real(); longlong val_int(); + const String *const_ptr_string() const + { + return &str_value; + } String *val_str(String*) { - DBUG_ASSERT(fixed == 1); return (String*) &str_value; } my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return get_date_from_string(ltime, fuzzydate); + return get_date_from_string(thd, ltime, fuzzydate); } int save_in_field(Field *field, bool no_conversions); const Type_handler *type_handler() const { return &type_handler_varchar; } - bool basic_const_item() const { return 1; } - bool eq(const Item *item, bool binary_cmp) const - { - return str_eq(&str_value, item, binary_cmp); - } Item *clone_item(THD *thd); Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs) { @@ -3977,7 +4279,6 @@ public: max_length= str_value.numchars() * collation.collation->mbmaxlen; } virtual void print(String *str, enum_query_type query_type); - bool check_partition_func_processor(void *int_arg) {return FALSE;} /** Return TRUE if character-set-introducer was explicitly specified in the @@ -4006,34 +4307,6 @@ public: String *check_well_formed_result(bool send_error) { return Item::check_well_formed_result(&str_value, send_error); } - enum_field_types odbc_temporal_literal_type(const LEX_CSTRING *type_str) const - { - /* - If string is a reasonably short pure ASCII string literal, - try to parse known ODBC style date, time or timestamp literals, - e.g: - SELECT {d'2001-01-01'}; - SELECT {t'10:20:30'}; - SELECT {ts'2001-01-01 10:20:30'}; - */ - if (collation.repertoire == MY_REPERTOIRE_ASCII && - str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4) - { - if (type_str->length == 1) - { - if (type_str->str[0] == 'd') /* {d'2001-01-01'} */ - return MYSQL_TYPE_DATE; - else if (type_str->str[0] == 't') /* {t'10:20:30'} */ - return MYSQL_TYPE_TIME; - } - else if (type_str->length == 2) /* {ts'2001-01-01 10:20:30'} */ - { - if (type_str->str[0] == 't' && type_str->str[1] == 's') - return MYSQL_TYPE_DATETIME; - } - } - return MYSQL_TYPE_STRING; // Not a temporal literal - } Item_basic_constant *make_string_literal_concat(THD *thd, const LEX_CSTRING *); Item *make_odbc_literal(THD *thd, const LEX_CSTRING *typestr); @@ -4220,38 +4493,30 @@ public: /** Item_hex_constant -- a common class for hex literals: X'HHHH' and 0xHHHH */ -class Item_hex_constant: public Item_basic_constant +class Item_hex_constant: public Item_literal { private: void hex_string_init(THD *thd, const char *str, size_t str_length); public: - Item_hex_constant(THD *thd): Item_basic_constant(thd) + Item_hex_constant(THD *thd): Item_literal(thd) { hex_string_init(thd, "", 0); } Item_hex_constant(THD *thd, const char *str, size_t str_length): - Item_basic_constant(thd) + Item_literal(thd) { hex_string_init(thd, str, str_length); } - enum Type type() const { return VARBIN_ITEM; } const Type_handler *type_handler() const { return &type_handler_varchar; } virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs) { return const_charset_converter(thd, tocs, true); } - bool check_partition_func_processor(void *int_arg) {return FALSE;} - bool basic_const_item() const { return 1; } - bool eq(const Item *item, bool binary_cmp) const - { - return item->basic_const_item() && item->type() == type() && - item->cast_to_int_type_handler() == cast_to_int_type_handler() && - str_value.bin_eq(&((Item_hex_constant*)item)->str_value); - } - String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + const String *const_ptr_string() const { return &str_value; } + String *val_str(String*) { return &str_value; } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } }; @@ -4267,22 +4532,18 @@ public: Item_hex_hybrid(THD *thd): Item_hex_constant(thd) {} Item_hex_hybrid(THD *thd, const char *str, size_t str_length): Item_hex_constant(thd, str, str_length) {} + const Type_handler *type_handler() const { return &type_handler_hex_hybrid; } uint decimal_precision() const; double val_real() { - DBUG_ASSERT(fixed == 1); return (double) (ulonglong) Item_hex_hybrid::val_int(); } longlong val_int() { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); return longlong_from_hex_hybrid(str_value.ptr(), str_value.length()); } my_decimal *val_decimal(my_decimal *decimal_value) { - // following assert is redundant, because fixed=1 assigned in constructor - DBUG_ASSERT(fixed == 1); longlong value= Item_hex_hybrid::val_int(); int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value); return decimal_value; @@ -4292,14 +4553,6 @@ public: field->set_notnull(); return field->store_hex_hybrid(str_value.ptr(), str_value.length()); } - const Type_handler *cast_to_int_type_handler() const - { - return &type_handler_longlong; - } - const Type_handler *type_handler_for_system_time() const - { - return &type_handler_longlong; - } void print(String *str, enum_query_type query_type); Item *get_copy(THD *thd) { return get_item_copy<Item_hex_hybrid>(thd, this); } @@ -4323,12 +4576,10 @@ public: Item_hex_constant(thd, str, str_length) {} longlong val_int() { - DBUG_ASSERT(fixed == 1); return longlong_from_string_with_check(&str_value); } double val_real() { - DBUG_ASSERT(fixed == 1); return double_from_string_with_check(&str_value); } my_decimal *val_decimal(my_decimal *decimal_value) @@ -4354,7 +4605,7 @@ public: }; -class Item_temporal_literal :public Item_basic_constant +class Item_temporal_literal :public Item_literal { protected: MYSQL_TIME cached_time; @@ -4364,37 +4615,21 @@ public: @param ltime DATE value. */ Item_temporal_literal(THD *thd, const MYSQL_TIME *ltime) - :Item_basic_constant(thd) + :Item_literal(thd) { collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII); decimals= 0; cached_time= *ltime; } Item_temporal_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg): - Item_basic_constant(thd) + Item_literal(thd) { collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII); decimals= dec_arg; cached_time= *ltime; } - bool basic_const_item() const { return true; } - bool const_item() const { return true; } - enum Type type() const { return DATE_ITEM; } - bool eq(const Item *item, bool binary_cmp) const; - bool check_partition_func_processor(void *int_arg) {return FALSE;} - - bool is_null() - { return is_null_from_temporal(); } - bool get_date_with_sql_mode(MYSQL_TIME *to); - String *val_str(String *str) - { return val_string_from_date(str); } - longlong val_int() - { return val_int_from_date(); } - double val_real() - { return val_real_from_date(); } - my_decimal *val_decimal(my_decimal *decimal_value) - { return val_decimal_from_date(decimal_value); } + const MYSQL_TIME *const_ptr_mysql_time() const { return &cached_time; } int save_in_field(Field *field, bool no_conversions) { return save_date_in_field(field, no_conversions); } }; @@ -4410,7 +4645,6 @@ public: :Item_temporal_literal(thd, ltime) { max_length= MAX_DATE_WIDTH; - fixed= 1; /* If date has zero month or day, it can return NULL in case of NO_ZERO_DATE or NO_ZERO_IN_DATE. @@ -4423,7 +4657,11 @@ public: const Type_handler *type_handler() const { return &type_handler_newdate; } void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + longlong val_int() { return Date(this).to_longlong(); } + double val_real() { return Date(this).to_double(); } + String *val_str(String *to) { return Date(this).to_string(to); } + my_decimal *val_decimal(my_decimal *to) { return Date(this).to_decimal(to); } + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_date_literal>(thd, this); } }; @@ -4439,12 +4677,15 @@ public: Item_temporal_literal(thd, ltime, dec_arg) { max_length= MIN_TIME_WIDTH + (decimals ? decimals + 1 : 0); - fixed= 1; } const Type_handler *type_handler() const { return &type_handler_time2; } void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + longlong val_int() { return Time(this).to_longlong(); } + double val_real() { return Time(this).to_double(); } + String *val_str(String *to) { return Time(this).to_string(to, decimals); } + my_decimal *val_decimal(my_decimal *to) { return Time(this).to_decimal(to); } + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_time_literal>(thd, this); } }; @@ -4460,14 +4701,23 @@ public: Item_temporal_literal(thd, ltime, dec_arg) { max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0); - fixed= 1; // See the comment on maybe_null in Item_date_literal maybe_null= !ltime->month || !ltime->day; } const Type_handler *type_handler() const { return &type_handler_datetime2; } void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + longlong val_int() { return Datetime(this).to_longlong(); } + double val_real() { return Datetime(this).to_double(); } + String *val_str(String *to) + { + return Datetime(this).to_string(to, decimals); + } + my_decimal *val_decimal(my_decimal *to) + { + return Datetime(this).to_decimal(to); + } + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_datetime_literal>(thd, this); } }; @@ -4504,7 +4754,7 @@ class Item_date_literal_for_invalid_dates: public Item_date_literal public: Item_date_literal_for_invalid_dates(THD *thd, const MYSQL_TIME *ltime) :Item_date_literal(thd, ltime) { } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { *ltime= cached_time; return (null_value= false); @@ -4522,7 +4772,7 @@ public: Item_datetime_literal_for_invalid_dates(THD *thd, const MYSQL_TIME *ltime, uint dec_arg) :Item_datetime_literal(thd, ltime, dec_arg) { } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { *ltime= cached_time; return (null_value= false); @@ -4717,8 +4967,10 @@ struct st_sp_security_context; class Item_sp { -public: +protected: + // Can be NULL in some non-SELECT queries Name_resolution_context *context; +public: sp_name *m_name; sp_head *m_sp; TABLE *dummy_table; @@ -4740,9 +4992,15 @@ public: bool execute_impl(THD *thd, Item **args, uint arg_count); bool init_result_field(THD *thd, uint max_length, uint maybe_null, bool *null_value, LEX_CSTRING *name); + void process_error(THD *thd) + { + if (context) + context->process_error(thd); + } }; -class Item_ref :public Item_ident +class Item_ref :public Item_ident, + protected With_sum_func_cache { protected: void set_properties(); @@ -4778,7 +5036,8 @@ public: /* Constructor need to process subselect with temporary tables (see Item) */ Item_ref(THD *thd, Item_ref *item) - :Item_ident(thd, item), set_properties_only(0), ref(item->ref) {} + :Item_ident(thd, item), With_sum_func_cache(*item), + set_properties_only(0), ref(item->ref) {} enum Type type() const { return REF_ITEM; } enum Type real_type() const { return ref ? (*ref)->type() : REF_ITEM; } @@ -4795,7 +5054,7 @@ public: bool val_bool(); String *val_str(String* tmp); bool is_null(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); double val_result(); longlong val_int_result(); String *str_result(String* tmp); @@ -4816,6 +5075,8 @@ public: Field *get_tmp_table_field() { return result_field ? result_field : (*ref)->get_tmp_table_field(); } Item *get_tmp_table_item(THD *thd); + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param); table_map used_tables() const; void update_used_tables(); COND *build_equal_items(THD *thd, COND_EQUAL *inherited, @@ -4945,6 +5206,8 @@ public: } bool excl_dep_on_grouping_fields(st_select_lex *sel) { return (*ref)->excl_dep_on_grouping_fields(sel); } + bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) + { return (*ref)->excl_dep_on_in_subq_left_part(subq_pred); } bool cleanup_excluding_fields_processor(void *arg) { Item *item= real_item(); @@ -4961,6 +5224,8 @@ public: return 0; return cleanup_processor(arg); } + bool with_sum_func() const { return m_with_sum_func; } + With_sum_func_cache* get_with_sum_func_cache() { return this; } }; @@ -5000,7 +5265,7 @@ public: my_decimal *val_decimal(my_decimal *); bool val_bool(); bool is_null(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); virtual Ref_Type ref_type() { return DIRECT_REF; } Item *get_copy(THD *thd) { return get_item_copy<Item_direct_ref>(thd, this); } @@ -5048,7 +5313,8 @@ class Expression_cache_tracker; */ class Item_cache_wrapper :public Item_result_field, - public With_subquery_cache + public With_subquery_cache, + protected With_sum_func_cache { private: /* Pointer on the cached expression */ @@ -5076,6 +5342,8 @@ public: enum Type type() const { return EXPR_CACHE_ITEM; } enum Type real_type() const { return orig_item->type(); } bool with_subquery() const { DBUG_ASSERT(fixed); return m_with_subquery; } + bool with_sum_func() const { return m_with_sum_func; } + With_sum_func_cache* get_with_sum_func_cache() { return this; } bool set_cache(THD *thd); Expression_cache_tracker* init_tracker(MEM_ROOT *mem_root); @@ -5093,7 +5361,7 @@ public: my_decimal *val_decimal(my_decimal *); bool val_bool(); bool is_null(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool send(Protocol *protocol, st_value *buffer); void save_org_in_field(Field *field, fast_field_copier data __attribute__ ((__unused__))) @@ -5251,10 +5519,12 @@ public: } bool excl_dep_on_table(table_map tab_map); bool excl_dep_on_grouping_fields(st_select_lex *sel); + bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred); Item *derived_field_transformer_for_having(THD *thd, uchar *arg); Item *derived_field_transformer_for_where(THD *thd, uchar *arg); - Item *derived_grouping_field_transformer_for_where(THD *thd, - uchar *arg); + Item *grouping_field_transformer_for_where(THD *thd, uchar *arg); + Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg); + Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg); void save_val(Field *to) { @@ -5305,14 +5575,14 @@ public: else return Item_direct_ref::is_null(); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (check_null_ref()) { bzero((char*) ltime,sizeof(*ltime)); return 1; } - return Item_direct_ref::get_date(ltime, fuzzydate); + return Item_direct_ref::get_date(thd, ltime, fuzzydate); } bool send(Protocol *protocol, st_value *buffer); void save_org_in_field(Field *field, @@ -5428,7 +5698,7 @@ public: String* val_str(String* s); my_decimal *val_decimal(my_decimal *); bool val_bool(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); virtual void print(String *str, enum_query_type query_type); table_map used_tables() const; Item *get_copy(THD *thd) @@ -5520,12 +5790,12 @@ protected: */ Item_copy(THD *thd, Item *i): Item(thd) { + DBUG_ASSERT(i->is_fixed()); item= i; null_value=maybe_null=item->maybe_null; Type_std_attributes::set(item); name= item->name; set_handler(item->type_handler()); - fixed= item->fixed; } public: @@ -5546,6 +5816,12 @@ public: const Type_handler *type_handler() const { return Type_handler_hybrid_field_type::type_handler(); } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + DBUG_ASSERT(0); + return NULL; + } void make_send_field(THD *thd, Send_field *field) { item->make_send_field(thd, field); } table_map used_tables() const { return (table_map) 1L; } @@ -5587,8 +5863,8 @@ public: my_decimal *val_decimal(my_decimal *); double val_real(); longlong val_int(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_string(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_string(thd, ltime, fuzzydate); } void copy(); int save_in_field(Field *field, bool no_conversions); Item *get_copy(THD *thd) @@ -5729,7 +6005,7 @@ public: double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *decimal_value); - bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate); bool send(Protocol *protocol, st_value *buffer); int save_in_field(Field *field_arg, bool no_conversions); bool save_in_param(THD *thd, Item_param *param) @@ -5781,7 +6057,7 @@ public: double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *decimal_value); - bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate); bool send(Protocol *protocol, st_value *buffer); }; @@ -5927,7 +6203,7 @@ public: for any value. */ -class Item_cache: public Item_basic_constant, +class Item_cache: public Item, public Type_handler_hybrid_field_type { protected: @@ -5946,25 +6222,27 @@ protected: cache_value() will set this flag to TRUE. */ bool value_cached; + + table_map used_table_map; public: Item_cache(THD *thd): - Item_basic_constant(thd), + Item(thd), Type_handler_hybrid_field_type(&type_handler_string), example(0), cached_field(0), - value_cached(0) + value_cached(0), + used_table_map(0) { - fixed= 1; maybe_null= 1; null_value= 1; } protected: Item_cache(THD *thd, const Type_handler *handler): - Item_basic_constant(thd), + Item(thd), Type_handler_hybrid_field_type(handler), example(0), cached_field(0), - value_cached(0) + value_cached(0), + used_table_map(0) { - fixed= 1; maybe_null= 1; null_value= 1; } @@ -5979,10 +6257,18 @@ public: cached_field= ((Item_field *)item)->field; return 0; }; + + void set_used_tables(table_map map) { used_table_map= map; } + table_map used_tables() const { return used_table_map; } enum Type type() const { return CACHE_ITEM; } const Type_handler *type_handler() const { return Type_handler_hybrid_field_type::type_handler(); } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + return create_tmp_field_ex_simple(table, src, param); + } virtual void keep_array() {} virtual void print(String *str, enum_query_type query_type); @@ -6013,7 +6299,7 @@ public: void cleanup() { clear(); - Item_basic_constant::cleanup(); + Item::cleanup(); } /** Check if saved item has a non-NULL value. @@ -6065,7 +6351,11 @@ public: { return convert_to_basic_const_item(thd); } Item *derived_field_transformer_for_where(THD *thd, uchar *arg) { return convert_to_basic_const_item(thd); } - Item *derived_grouping_field_transformer_for_where(THD *thd, uchar *arg) + Item *grouping_field_transformer_for_where(THD *thd, uchar *arg) + { return convert_to_basic_const_item(thd); } + Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg) + { return convert_to_basic_const_item(thd); } + Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg) { return convert_to_basic_const_item(thd); } }; @@ -6084,8 +6374,8 @@ public: longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_int(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_int(thd, ltime, fuzzydate); } bool cache_value(); int save_in_field(Field *field, bool no_conversions); Item *convert_to_basic_const_item(THD *thd); @@ -6097,9 +6387,13 @@ public: class Item_cache_year: public Item_cache_int { public: - Item_cache_year(THD *thd): Item_cache_int(thd, &type_handler_year) { } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_year(ltime, fuzzydate); } + Item_cache_year(THD *thd, const Type_handler *handler) + :Item_cache_int(thd, handler) { } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { + return null_value= + VYear(this).to_mysql_time_with_warn(thd, ltime, fuzzydate, NULL); + } }; @@ -6108,14 +6402,8 @@ class Item_cache_temporal: public Item_cache_int protected: Item_cache_temporal(THD *thd, const Type_handler *handler); public: - String* val_str(String *str); - my_decimal *val_decimal(my_decimal *); - longlong val_int(); - longlong val_datetime_packed(); - longlong val_time_packed(); - double val_real(); bool cache_value(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); int save_in_field(Field *field, bool no_conversions); void store_packed(longlong val_arg, Item *example); /* @@ -6138,6 +6426,31 @@ public: Item *get_copy(THD *thd) { return get_item_copy<Item_cache_time>(thd, this); } Item *make_literal(THD *); + longlong val_datetime_packed(THD *thd) + { + date_mode_t fuzzy= Datetime::comparison_flags_for_get_date(); + return has_value() ? Datetime(thd, this, fuzzy).to_packed() : 0; + } + longlong val_time_packed(THD *thd) + { + return has_value() ? value : 0; + } + longlong val_int() + { + return has_value() ? Time(this).to_longlong() : 0; + } + double val_real() + { + return has_value() ? Time(this).to_double() : 0; + } + String *val_str(String *to) + { + return has_value() ? Time(this).to_string(to, decimals) : NULL; + } + my_decimal *val_decimal(my_decimal *to) + { + return has_value() ? Time(this).to_decimal(to) : NULL; + } }; @@ -6149,6 +6462,30 @@ public: Item *get_copy(THD *thd) { return get_item_copy<Item_cache_datetime>(thd, this); } Item *make_literal(THD *); + longlong val_datetime_packed(THD *thd) + { + return has_value() ? value : 0; + } + longlong val_time_packed(THD *thd) + { + return Time(thd, this, Time::comparison_flags_for_get_date()).to_packed(); + } + longlong val_int() + { + return has_value() ? Datetime(this).to_longlong() : 0; + } + double val_real() + { + return has_value() ? Datetime(this).to_double() : 0; + } + String *val_str(String *to) + { + return has_value() ? Datetime(this).to_string(to, decimals) : NULL; + } + my_decimal *val_decimal(my_decimal *to) + { + return has_value() ? Datetime(this).to_decimal(to) : NULL; + } }; @@ -6160,6 +6497,24 @@ public: Item *get_copy(THD *thd) { return get_item_copy<Item_cache_date>(thd, this); } Item *make_literal(THD *); + longlong val_datetime_packed(THD *thd) + { + return has_value() ? value : 0; + } + longlong val_time_packed(THD *thd) + { + return Time(thd, this, Time::comparison_flags_for_get_date()).to_packed(); + } + longlong val_int() { return has_value() ? Date(this).to_longlong() : 0; } + double val_real() { return has_value() ? Date(this).to_double() : 0; } + String *val_str(String *to) + { + return has_value() ? Date(this).to_string(to) : NULL; + } + my_decimal *val_decimal(my_decimal *to) + { + return has_value() ? Date(this).to_decimal(to) : NULL; + } }; @@ -6174,8 +6529,8 @@ public: longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_real(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_real(thd, ltime, fuzzydate); } bool cache_value(); Item *convert_to_basic_const_item(THD *thd); Item *get_copy(THD *thd) @@ -6194,8 +6549,8 @@ public: longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_decimal(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return VDec(this).to_datetime_with_warn(thd, ltime, fuzzydate, this); } bool cache_value(); Item *convert_to_basic_const_item(THD *thd); Item *get_copy(THD *thd) @@ -6222,8 +6577,8 @@ public: longlong val_int(); String* val_str(String *); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_string(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_string(thd, ltime, fuzzydate); } CHARSET_INFO *charset() const { return value->charset(); }; int save_in_field(Field *field, bool no_conversions); bool cache_value(); @@ -6304,7 +6659,7 @@ public: illegal_method_call((const char*)"val_decimal"); return 0; }; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { illegal_method_call((const char*)"val_decimal"); return true; @@ -6353,7 +6708,7 @@ public: Type_handler_hybrid_field_type(item->real_type_handler()), enum_set_typelib(0) { - DBUG_ASSERT(item->fixed); + DBUG_ASSERT(item->is_fixed()); maybe_null= item->maybe_null; } Item_type_holder(THD *thd, @@ -6387,8 +6742,9 @@ public: longlong val_int(); my_decimal *val_decimal(my_decimal *); String *val_str(String*); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - Field *create_tmp_field(bool group, TABLE *table) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) { return Item_type_holder::real_type_handler()-> make_and_init_table_field(&name, Record_addr(maybe_null), @@ -6528,4 +6884,39 @@ inline void Virtual_column_info::print(String* str) expr->print_for_table_def(str); } +inline bool TABLE::mark_column_with_deps(Field *field) +{ + bool res; + if (!(res= bitmap_fast_test_and_set(read_set, field->field_index))) + { + if (field->vcol_info) + mark_virtual_column_deps(field); + } + return res; +} + +inline bool TABLE::mark_virtual_column_with_deps(Field *field) +{ + bool res; + DBUG_ASSERT(field->vcol_info); + if (!(res= bitmap_fast_test_and_set(read_set, field->field_index))) + mark_virtual_column_deps(field); + return res; +} + +inline void TABLE::mark_virtual_column_deps(Field *field) +{ + DBUG_ASSERT(field->vcol_info); + DBUG_ASSERT(field->vcol_info->expr); + field->vcol_info->expr->walk(&Item::register_field_in_read_map, 1, 0); +} + +inline void TABLE::use_all_stored_columns() +{ + bitmap_set_all(read_set); + if (Field **vf= vfield) + for (; *vf; vf++) + bitmap_clear_bit(read_set, (*vf)->field_index); +} + #endif /* SQL_ITEM_INCLUDED */ diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 4d03462d7c3..3467fda79c7 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -225,16 +225,15 @@ Cached_item_decimal::Cached_item_decimal(Item *it) bool Cached_item_decimal::cmp() { - my_decimal tmp; - my_decimal *ptmp= item->val_decimal(&tmp); - if (null_value != item->null_value || - (!item->null_value && my_decimal_cmp(&value, ptmp))) + VDec tmp(item); + if (null_value != tmp.is_null() || + (!tmp.is_null() && tmp.cmp(&value))) { - null_value= item->null_value; + null_value= tmp.is_null(); /* Save only not null values */ if (!null_value) { - my_decimal2decimal(ptmp, &value); + my_decimal2decimal(tmp.ptr(), &value); return TRUE; } return FALSE; @@ -245,17 +244,9 @@ bool Cached_item_decimal::cmp() int Cached_item_decimal::cmp_read_only() { - my_decimal tmp; - my_decimal *ptmp= item->val_decimal(&tmp); + VDec tmp(item); if (null_value) - { - if (item->null_value) - return 0; - else - return -1; - } - if (item->null_value) - return 1; - return my_decimal_cmp(&value, ptmp); + return tmp.is_null() ? 0 : -1; + return tmp.is_null() ? 1 : value.cmp(tmp.ptr()); } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index ccbae1bad34..f5c49214076 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -34,29 +34,6 @@ #include "sql_time.h" // make_truncated_value_warning #include "sql_base.h" // dynamic_column_error_message -/** - find an temporal type (item) that others will be converted to - for the purpose of comparison. - - this is the type that will be used in warnings like - "Incorrect <<TYPE>> value". -*/ -static Item *find_date_time_item(Item **args, uint nargs, uint col) -{ - Item *date_arg= 0, **arg, **arg_end; - for (arg= args, arg_end= args + nargs; arg != arg_end ; arg++) - { - Item *item= arg[0]->element_index(col); - if (item->cmp_type() != TIME_RESULT) - continue; - if (item->field_type() == MYSQL_TYPE_DATETIME) - return item; - if (!date_arg) - date_arg= item; - } - return date_arg; -} - /* Compare row signature of two expressions @@ -707,10 +684,11 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value, int Arg_comparator::compare_time() { - longlong val1= (*a)->val_time_packed(); + THD *thd= current_thd; + longlong val1= (*a)->val_time_packed(thd); if (!(*a)->null_value) { - longlong val2= (*b)->val_time_packed(); + longlong val2= (*b)->val_time_packed(thd); if (!(*b)->null_value) return compare_not_null_values(val1, val2); } @@ -722,8 +700,9 @@ int Arg_comparator::compare_time() int Arg_comparator::compare_e_time() { - longlong val1= (*a)->val_time_packed(); - longlong val2= (*b)->val_time_packed(); + THD *thd= current_thd; + longlong val1= (*a)->val_time_packed(thd); + longlong val2= (*b)->val_time_packed(thd); if ((*a)->null_value || (*b)->null_value) return MY_TEST((*a)->null_value && (*b)->null_value); return MY_TEST(val1 == val2); @@ -733,10 +712,11 @@ int Arg_comparator::compare_e_time() int Arg_comparator::compare_datetime() { - longlong val1= (*a)->val_datetime_packed(); + THD *thd= current_thd; + longlong val1= (*a)->val_datetime_packed(thd); if (!(*a)->null_value) { - longlong val2= (*b)->val_datetime_packed(); + longlong val2= (*b)->val_datetime_packed(thd); if (!(*b)->null_value) return compare_not_null_values(val1, val2); } @@ -748,8 +728,9 @@ int Arg_comparator::compare_datetime() int Arg_comparator::compare_e_datetime() { - longlong val1= (*a)->val_datetime_packed(); - longlong val2= (*b)->val_datetime_packed(); + THD *thd= current_thd; + longlong val1= (*a)->val_datetime_packed(thd); + longlong val2= (*b)->val_datetime_packed(thd); if ((*a)->null_value || (*b)->null_value) return MY_TEST((*a)->null_value && (*b)->null_value); return MY_TEST(val1 == val2); @@ -818,17 +799,15 @@ int Arg_comparator::compare_real() int Arg_comparator::compare_decimal() { - my_decimal decimal1; - my_decimal *val1= (*a)->val_decimal(&decimal1); - if (!(*a)->null_value) + VDec val1(*a); + if (!val1.is_null()) { - my_decimal decimal2; - my_decimal *val2= (*b)->val_decimal(&decimal2); - if (!(*b)->null_value) + VDec val2(*b); + if (!val2.is_null()) { if (set_null) owner->null_value= 0; - return my_decimal_cmp(val1, val2); + return val1.cmp(val2); } } if (set_null) @@ -847,12 +826,10 @@ int Arg_comparator::compare_e_real() int Arg_comparator::compare_e_decimal() { - my_decimal decimal1, decimal2; - my_decimal *val1= (*a)->val_decimal(&decimal1); - my_decimal *val2= (*b)->val_decimal(&decimal2); - if ((*a)->null_value || (*b)->null_value) - return MY_TEST((*a)->null_value && (*b)->null_value); - return MY_TEST(my_decimal_cmp(val1, val2) == 0); + VDec val1(*a), val2(*b); + if (val1.is_null() || val2.is_null()) + return MY_TEST(val1.is_null() && val2.is_null()); + return MY_TEST(val1.cmp(val2) == 0); } @@ -1292,7 +1269,7 @@ bool Item_in_optimizer::fix_left(THD *thd) used_tables_cache= args[0]->used_tables(); } eval_not_null_tables(NULL); - with_sum_func= args[0]->with_sum_func; + copy_with_sum_func(args[0]); with_param= args[0]->with_param || args[1]->with_param; with_field= args[0]->with_field; if ((const_item_cache= args[0]->const_item())) @@ -1300,11 +1277,11 @@ bool Item_in_optimizer::fix_left(THD *thd) cache->store(args[0]); cache->cache_value(); } - if (args[1]->fixed) + if (args[1]->is_fixed()) { /* to avoid overriding is called to update left expression */ used_tables_and_const_cache_join(args[1]); - with_sum_func= with_sum_func || args[1]->with_sum_func; + join_with_sum_func(args[1]); } DBUG_RETURN(0); } @@ -1340,7 +1317,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) if (args[1]->maybe_null) maybe_null=1; m_with_subquery= true; - with_sum_func= with_sum_func || args[1]->with_sum_func; + join_with_sum_func(args[1]); with_field= with_field || args[1]->with_field; with_param= args[0]->with_param || args[1]->with_param; used_tables_and_const_cache_join(args[1]); @@ -1892,7 +1869,7 @@ bool Item_func_interval::fix_length_and_dec() max_length= 2; used_tables_and_const_cache_join(row); not_null_tables_cache= row->not_null_tables(); - with_sum_func= with_sum_func || row->with_sum_func; + join_with_sum_func(row); with_param= with_param || row->with_param; with_field= with_field || row->with_field; return FALSE; @@ -1971,11 +1948,11 @@ longlong Item_func_interval::val_int() ((el->result_type() == DECIMAL_RESULT) || (el->result_type() == INT_RESULT))) { - my_decimal e_dec_buf, *e_dec= el->val_decimal(&e_dec_buf); + VDec e_dec(el); /* Skip NULL ranges. */ - if (el->null_value) + if (e_dec.is_null()) continue; - if (my_decimal_cmp(e_dec, dec) > 0) + if (e_dec.cmp(dec) > 0) return i - 1; } else @@ -2121,23 +2098,27 @@ bool Item_func_between::fix_length_and_dec_temporal(THD *thd) } -longlong Item_func_between::val_int_cmp_temporal() +longlong Item_func_between::val_int_cmp_datetime() { - enum_field_types f_type= m_comparator.type_handler()->field_type(); - longlong value= args[0]->val_temporal_packed(f_type), a, b; + THD *thd= current_thd; + longlong value= args[0]->val_datetime_packed(thd), a, b; if ((null_value= args[0]->null_value)) return 0; - a= args[1]->val_temporal_packed(f_type); - b= args[2]->val_temporal_packed(f_type); - if (!args[1]->null_value && !args[2]->null_value) - return (longlong) ((value >= a && value <= b) != negated); - if (args[1]->null_value && args[2]->null_value) - null_value= true; - else if (args[1]->null_value) - null_value= value <= b; // not null if false range. - else - null_value= value >= a; - return (longlong) (!null_value && negated); + a= args[1]->val_datetime_packed(thd); + b= args[2]->val_datetime_packed(thd); + return val_int_cmp_int_finalize(value, a, b); +} + + +longlong Item_func_between::val_int_cmp_time() +{ + THD *thd= current_thd; + longlong value= args[0]->val_time_packed(thd), a, b; + if ((null_value= args[0]->null_value)) + return 0; + a= args[1]->val_time_packed(thd); + b= args[2]->val_time_packed(thd); + return val_int_cmp_int_finalize(value, a, b); } @@ -2176,39 +2157,41 @@ longlong Item_func_between::val_int_cmp_int() return 0; /* purecov: inspected */ a= args[1]->val_int(); b= args[2]->val_int(); + return val_int_cmp_int_finalize(value, a, b); +} + + +bool Item_func_between::val_int_cmp_int_finalize(longlong value, + longlong a, + longlong b) +{ if (!args[1]->null_value && !args[2]->null_value) return (longlong) ((value >= a && value <= b) != negated); if (args[1]->null_value && args[2]->null_value) null_value= true; else if (args[1]->null_value) - { null_value= value <= b; // not null if false range. - } else - { null_value= value >= a; - } return (longlong) (!null_value && negated); } longlong Item_func_between::val_int_cmp_decimal() { - my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf), - a_buf, *a_dec, b_buf, *b_dec; - if ((null_value=args[0]->null_value)) + VDec dec(args[0]); + if ((null_value= dec.is_null())) return 0; /* purecov: inspected */ - a_dec= args[1]->val_decimal(&a_buf); - b_dec= args[2]->val_decimal(&b_buf); - if (!args[1]->null_value && !args[2]->null_value) - return (longlong) ((my_decimal_cmp(dec, a_dec) >= 0 && - my_decimal_cmp(dec, b_dec) <= 0) != negated); - if (args[1]->null_value && args[2]->null_value) + VDec a_dec(args[1]), b_dec(args[2]); + if (!a_dec.is_null() && !b_dec.is_null()) + return (longlong) ((dec.cmp(a_dec) >= 0 && + dec.cmp(b_dec) <= 0) != negated); + if (a_dec.is_null() && b_dec.is_null()) null_value= true; - else if (args[1]->null_value) - null_value= (my_decimal_cmp(dec, b_dec) <= 0); + else if (a_dec.is_null()) + null_value= (dec.cmp(b_dec) <= 0); else - null_value= (my_decimal_cmp(dec, a_dec) >= 0); + null_value= (dec.cmp(a_dec) >= 0); return (longlong) (!null_value && negated); } @@ -2316,12 +2299,12 @@ Item_func_ifnull::str_op(String *str) } -bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_func_ifnull::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); for (uint i= 0; i < 2; i++) { - Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); + Datetime dt(thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); if (!(dt.copy_to_mysql_time(ltime, mysql_timestamp_type()))) return (null_value= false); } @@ -2329,12 +2312,12 @@ bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) } -bool Item_func_ifnull::time_op(MYSQL_TIME *ltime) +bool Item_func_ifnull::time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); for (uint i= 0; i < 2; i++) { - if (!Time(args[i]).copy_to_mysql_time(ltime)) + if (!Time(thd, args[i]).copy_to_mysql_time(ltime)) return (null_value= false); } return (null_value= true); @@ -2816,23 +2799,23 @@ Item_func_nullif::decimal_op(my_decimal * decimal_value) bool -Item_func_nullif::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) +Item_func_nullif::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); if (!compare()) return (null_value= true); - Datetime dt(current_thd, args[2], fuzzydate); + Datetime dt(thd, args[2], fuzzydate); return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } bool -Item_func_nullif::time_op(MYSQL_TIME *ltime) +Item_func_nullif::time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); if (!compare()) return (null_value= true); - return (null_value= Time(args[2]).copy_to_mysql_time(ltime)); + return (null_value= Time(thd, args[2]).copy_to_mysql_time(ltime)); } @@ -2914,7 +2897,7 @@ Item *Item_func_case_simple::find_item() Item *Item_func_decode_oracle::find_item() { uint idx; - if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx)) + if (!Predicant_to_list_comparator::cmp_nulls_equal(current_thd, this, &idx)) return args[idx + when_count()]; Item **pos= Item_func_decode_oracle::else_expr_addr(); return pos ? pos[0] : 0; @@ -2990,24 +2973,24 @@ my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value) } -bool Item_func_case::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_func_case::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); Item *item= find_item(); if (!item) return (null_value= true); - Datetime dt(current_thd, item, fuzzydate); + Datetime dt(thd, item, fuzzydate); return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } -bool Item_func_case::time_op(MYSQL_TIME *ltime) +bool Item_func_case::time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); Item *item= find_item(); if (!item) return (null_value= true); - return (null_value= Time(item).copy_to_mysql_time(ltime)); + return (null_value= Time(thd, item).copy_to_mysql_time(ltime)); } @@ -3343,12 +3326,12 @@ double Item_func_coalesce::real_op() } -bool Item_func_coalesce::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_func_coalesce::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); for (uint i= 0; i < arg_count; i++) { - Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); + Datetime dt(thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); if (!dt.copy_to_mysql_time(ltime, mysql_timestamp_type())) return (null_value= false); } @@ -3356,12 +3339,12 @@ bool Item_func_coalesce::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) } -bool Item_func_coalesce::time_op(MYSQL_TIME *ltime) +bool Item_func_coalesce::time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); for (uint i= 0; i < arg_count; i++) { - if (!Time(args[i]).copy_to_mysql_time(ltime)) + if (!Time(thd, args[i]).copy_to_mysql_time(ltime)) return (null_value= false); } return (null_value= true); @@ -3649,7 +3632,7 @@ void in_datetime::set(uint pos,Item *item) { struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= item->val_datetime_packed(); + buff->val= item->val_datetime_packed(current_thd); buff->unsigned_flag= 1L; } @@ -3657,13 +3640,22 @@ void in_time::set(uint pos,Item *item) { struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= item->val_time_packed(); + buff->val= item->val_time_packed(current_thd); buff->unsigned_flag= 1L; } -uchar *in_temporal::get_value_internal(Item *item, enum_field_types f_type) +uchar *in_datetime::get_value(Item *item) { - tmp.val= item->val_temporal_packed(f_type); + tmp.val= item->val_datetime_packed(current_thd); + if (item->null_value) + return 0; + tmp.unsigned_flag= 1L; + return (uchar*) &tmp; +} + +uchar *in_time::get_value(Item *item) +{ + tmp.val= item->val_time_packed(current_thd); if (item->null_value) return 0; tmp.unsigned_flag= 1L; @@ -3875,39 +3867,15 @@ bool cmp_item_row::alloc_comparators(THD *thd, uint cols) void cmp_item_row::store_value(Item *item) { DBUG_ENTER("cmp_item_row::store_value"); - THD *thd= current_thd; - if (!alloc_comparators(thd, item->cols())) + DBUG_ASSERT(comparators); + DBUG_ASSERT(n == item->cols()); + item->bring_value(); + item->null_value= 0; + for (uint i=0; i < n; i++) { - item->bring_value(); - item->null_value= 0; - for (uint i=0; i < n; i++) - { - if (!comparators[i]) - { - /** - Comparators for the row elements that have temporal data types - are installed at initialization time by prepare_comparators(). - Here we install comparators for the other data types. - There is a bug in the below code. See MDEV-11511. - When performing: - (predicant0,predicant1) IN ((value00,value01),(value10,value11)) - It uses only the data type and the collation of the predicant - elements only. It should be fixed to aggregate the data type and - the collation for all elements at the N-th positions of the - predicate and all values: - - predicate0, value00, value01 - - predicate1, value10, value11 - */ - Item *elem= item->element_index(i); - const Type_handler *handler= elem->type_handler(); - DBUG_ASSERT(elem->cmp_type() != TIME_RESULT); - if (!(comparators[i]= - handler->make_cmp_item(thd, elem->collation.collation))) - break; // new failed - } - comparators[i]->store_value(item->element_index(i)); - item->null_value|= item->element_index(i)->null_value; - } + DBUG_ASSERT(comparators[i]); + comparators[i]->store_value(item->element_index(i)); + item->null_value|= item->element_index(i)->null_value; } DBUG_VOID_RETURN; } @@ -4000,9 +3968,8 @@ int cmp_item_decimal::cmp_not_null(const Value *val) int cmp_item_decimal::cmp(Item *arg) { - my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf); - return (m_null_value || arg->null_value) ? - UNKNOWN : (my_decimal_cmp(&value, tmp) != 0); + VDec tmp(arg); + return m_null_value || tmp.is_null() ? UNKNOWN : (tmp.cmp(&value) != 0); } @@ -4019,14 +3986,6 @@ cmp_item* cmp_item_decimal::make_same() } -void cmp_item_temporal::store_value_internal(Item *item, - enum_field_types f_type) -{ - value= item->val_temporal_packed(f_type); - m_null_value= item->null_value; -} - - int cmp_item_datetime::cmp_not_null(const Value *val) { DBUG_ASSERT(!val->is_null()); @@ -4037,7 +3996,7 @@ int cmp_item_datetime::cmp_not_null(const Value *val) int cmp_item_datetime::cmp(Item *arg) { - const bool rc= value != arg->val_datetime_packed(); + const bool rc= value != arg->val_datetime_packed(current_thd); return (m_null_value || arg->null_value) ? UNKNOWN : rc; } @@ -4052,7 +4011,7 @@ int cmp_item_time::cmp_not_null(const Value *val) int cmp_item_time::cmp(Item *arg) { - const bool rc= value != arg->val_time_packed(); + const bool rc= value != arg->val_time_packed(current_thd); return (m_null_value || arg->null_value) ? UNKNOWN : rc; } @@ -4296,25 +4255,84 @@ bool Item_func_in::value_list_convert_const_to_int(THD *thd) } -/** - Historically this code installs comparators at initialization time - for temporal ROW elements only. All other comparators are installed later, - during the first store_value(). This causes the bug MDEV-11511. - See also comments in cmp_item_row::store_value(). -*/ -bool cmp_item_row::prepare_comparators(THD *thd, Item **args, uint arg_count) +bool cmp_item_row:: + aggregate_row_elements_for_comparison(THD *thd, + Type_handler_hybrid_field_type *cmp, + Item_args *tmp, + const char *funcname, + uint col, + uint level) { + DBUG_EXECUTE_IF("cmp_item", + { + for (uint i= 0 ; i < tmp->argument_count(); i++) + { + Item *arg= tmp->arguments()[i]; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, "DBUG: %s[%d,%d] handler=%s", + String_space(level).c_ptr(), col, i, + arg->type_handler()->name().ptr()); + } + } + ); + bool err= cmp->aggregate_for_comparison(funcname, tmp->arguments(), + tmp->argument_count(), true); + DBUG_EXECUTE_IF("cmp_item", + { + if (!err) + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, "DBUG: %s=> handler=%s", + String_space(level).c_ptr(), + cmp->type_handler()->name().ptr()); + } + ); + return err; +} + + +bool cmp_item_row::prepare_comparators(THD *thd, const char *funcname, + const Item_args *args, uint level) +{ + DBUG_EXECUTE_IF("cmp_item", + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, "DBUG: %sROW(%d args) level=%d", + String_space(level).c_ptr(), + args->argument_count(), level);); + DBUG_ASSERT(args->argument_count() > 0); + if (alloc_comparators(thd, args->arguments()[0]->cols())) + return true; + DBUG_ASSERT(n == args->arguments()[0]->cols()); for (uint col= 0; col < n; col++) { - Item *date_arg= find_date_time_item(args, arg_count, col); - if (date_arg) + Item_args tmp; + Type_handler_hybrid_field_type cmp; + + if (tmp.alloc_and_extract_row_elements(thd, args, col) || + aggregate_row_elements_for_comparison(thd, &cmp, &tmp, + funcname, col, level + 1)) + return true; + + /* + There is a legacy bug (MDEV-11511) in the code below, + which should be fixed eventually. + When performing: + (predicant0,predicant1) IN ((value00,value01),(value10,value11)) + It uses only the data type and the collation of the predicant + elements only. It should be fixed to take into account the data type and + the collation for all elements at the N-th positions of the + predicate and all values: + - predicate0, value00, value01 + - predicate1, value10, value11 + */ + Item *item0= args->arguments()[0]->element_index(col); + CHARSET_INFO *collation= item0->collation.collation; + if (!(comparators[col]= cmp.type_handler()->make_cmp_item(thd, collation))) + return true; + if (cmp.type_handler() == &type_handler_row) { - // TODO: do like the scalar comparators do - const Type_handler *h= date_arg->type_handler(); - comparators[col]= h->field_type() == MYSQL_TYPE_TIME ? - (cmp_item *) new (thd->mem_root) cmp_item_time() : - (cmp_item *) new (thd->mem_root) cmp_item_datetime(); - if (!comparators[col]) + // Prepare comparators for ROW elements recursively + cmp_item_row *row= static_cast<cmp_item_row*>(comparators[col]); + if (row->prepare_comparators(thd, funcname, &tmp, level + 1)) return true; } } @@ -4324,19 +4342,10 @@ bool cmp_item_row::prepare_comparators(THD *thd, Item **args, uint arg_count) bool Item_func_in::fix_for_row_comparison_using_bisection(THD *thd) { - uint cols= args[0]->cols(); if (unlikely(!(array= new (thd->mem_root) in_row(thd, arg_count-1, 0)))) return true; cmp_item_row *cmp= &((in_row*)array)->tmp; - if (cmp->alloc_comparators(thd, cols) || - cmp->prepare_comparators(thd, args, arg_count)) - return true; - /* - Only DATETIME items comparators were initialized. - Call store_value() to setup others. - */ - cmp->store_value(args[0]); - if (unlikely(thd->is_fatal_error)) // OOM + if (cmp->prepare_comparators(thd, func_name(), this, 0)) return true; fix_in_vector(); return false; @@ -4375,8 +4384,7 @@ bool Item_func_in::fix_for_row_comparison_using_cmp_items(THD *thd) DBUG_ASSERT(get_comparator_type_handler(0) == &type_handler_row); DBUG_ASSERT(get_comparator_cmp_item(0)); cmp_item_row *cmp_row= (cmp_item_row*) get_comparator_cmp_item(0); - return cmp_row->alloc_comparators(thd, args[0]->cols()) || - cmp_row->prepare_comparators(thd, args, arg_count); + return cmp_row->prepare_comparators(thd, func_name(), this, 0); } @@ -4647,7 +4655,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) const_item_cache= FALSE; } - with_sum_func|= item->with_sum_func; + join_with_sum_func(item); with_param|= item->with_param; with_field|= item->with_field; m_with_subquery|= item->with_subquery(); @@ -6301,76 +6309,53 @@ void Item_equal::add_const(THD *thd, Item *c) equal_items.push_front(c, thd->mem_root); return; } - Item *const_item= get_const(); - switch (Item_equal::compare_type_handler()->cmp_type()) { - case TIME_RESULT: - { - enum_field_types f_type= context_field->field_type(); - longlong value0= c->val_temporal_packed(f_type); - longlong value1= const_item->val_temporal_packed(f_type); - cond_false= c->null_value || const_item->null_value || value0 != value1; - break; - } - case STRING_RESULT: - { - String *str1, *str2; - /* - Suppose we have an expression (with a string type field) like this: - WHERE field=const1 AND field=const2 ... - - For all pairs field=constXXX we know that: - - - Item_func_eq::fix_length_and_dec() performed collation and character - set aggregation and added character set converters when needed. - Note, the case like: - WHERE field=const1 COLLATE latin1_bin AND field=const2 - is not handled here, because the field would be replaced to - Item_func_set_collation, which cannot get into Item_equal. - So all constXXX that are handled by Item_equal - already have compatible character sets with "field". - - - Also, Field_str::test_if_equality_guarantees_uniqueness() guarantees - that the comparison collation of all equalities handled by Item_equal - match the the collation of the field. - - Therefore, at Item_equal::add_const() time all constants constXXX - should be directly comparable to each other without an additional - character set conversion. - It's safe to do val_str() for "const_item" and "c" and compare - them according to the collation of the *field*. - - So in a script like this: - CREATE TABLE t1 (a VARCHAR(10) COLLATE xxx); - INSERT INTO t1 VALUES ('a'),('A'); - SELECT * FROM t1 WHERE a='a' AND a='A'; - Item_equal::add_const() effectively rewrites the condition to: - SELECT * FROM t1 WHERE a='a' AND 'a' COLLATE xxx='A'; - and then to: - SELECT * FROM t1 WHERE a='a'; // if the two constants were equal - // e.g. in case of latin1_swedish_ci - or to: - SELECT * FROM t1 WHERE FALSE; // if the two constants were not equal - // e.g. in case of latin1_bin - - Note, both "const_item" and "c" can return NULL, e.g.: - SELECT * FROM t1 WHERE a=NULL AND a='const'; - SELECT * FROM t1 WHERE a='const' AND a=NULL; - SELECT * FROM t1 WHERE a='const' AND a=(SELECT MAX(a) FROM t2) - */ - cond_false= !(str1= const_item->val_str(&cmp_value1)) || - !(str2= c->val_str(&cmp_value2)) || - !str1->eq(str2, compare_collation()); - break; - } - default: - { - Item_func_eq *func= new (thd->mem_root) Item_func_eq(thd, c, const_item); - if (func->set_cmp_func()) - return; - func->quick_fix_field(); - cond_false= !func->val_int(); - } - } + + /* + Suppose we have an expression (with a string type field) like this: + WHERE field=const1 AND field=const2 ... + + For all pairs field=constXXX we know that: + + - Item_func_eq::fix_length_and_dec() performed collation and character + set aggregation and added character set converters when needed. + Note, the case like: + WHERE field=const1 COLLATE latin1_bin AND field=const2 + is not handled here, because the field would be replaced to + Item_func_set_collation, which cannot get into Item_equal. + So all constXXX that are handled by Item_equal + already have compatible character sets with "field". + + - Also, Field_str::test_if_equality_guarantees_uniqueness() guarantees + that the comparison collation of all equalities handled by Item_equal + match the the collation of the field. + + Therefore, at Item_equal::add_const() time all constants constXXX + should be directly comparable to each other without an additional + character set conversion. + It's safe to do val_str() for "const_item" and "c" and compare + them according to the collation of the *field*. + + So in a script like this: + CREATE TABLE t1 (a VARCHAR(10) COLLATE xxx); + INSERT INTO t1 VALUES ('a'),('A'); + SELECT * FROM t1 WHERE a='a' AND a='A'; + Item_equal::add_const() effectively rewrites the condition to: + SELECT * FROM t1 WHERE a='a' AND 'a' COLLATE xxx='A'; + and then to: + SELECT * FROM t1 WHERE a='a'; // if the two constants were equal + // e.g. in case of latin1_swedish_ci + or to: + SELECT * FROM t1 WHERE FALSE; // if the two constants were not equal + // e.g. in case of latin1_bin + + Note, both "const_item" and "c" can return NULL, e.g.: + SELECT * FROM t1 WHERE a=NULL AND a='const'; + SELECT * FROM t1 WHERE a='const' AND a=NULL; + SELECT * FROM t1 WHERE a='const' AND a=(SELECT MAX(a) FROM t2) + */ + + cond_false= !Item_equal::compare_type_handler()->Item_eq_value(thd, this, c, + get_const()); if (with_const && equal_items.elements == 1) cond_true= TRUE; if (cond_false || cond_true) @@ -6675,7 +6660,7 @@ bool Item_equal::fix_fields(THD *thd, Item **ref) used_tables_cache|= item->used_tables(); tmp_table_map= item->not_null_tables(); not_null_tables_cache|= tmp_table_map; - DBUG_ASSERT(!item->with_sum_func && !item->with_subquery()); + DBUG_ASSERT(!item->with_sum_func() && !item->with_subquery()); if (item->maybe_null) maybe_null= 1; if (!item->get_item_equal()) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 60b56090a23..cf210a71433 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -152,7 +152,8 @@ public: class SEL_ARG; struct KEY_PART; -class Item_bool_func :public Item_int_func +class Item_bool_func :public Item_int_func, + public Type_cmp_attributes { protected: /* @@ -215,9 +216,9 @@ public: Item_bool_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {} Item_bool_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { } Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {} - const Type_handler *type_handler() const { return &type_handler_long; } - bool is_bool_type() { return true; } - virtual CHARSET_INFO *compare_collation() const { return NULL; } + const Type_handler *type_handler() const { return &type_handler_bool; } + const Type_handler *fixed_type_handler() const { return &type_handler_bool; } + CHARSET_INFO *compare_collation() const { return NULL; } bool fix_length_and_dec() { decimals=0; max_length=1; return FALSE; } uint decimal_precision() const { return 1; } bool need_parentheses_in_default() { return true; } @@ -891,6 +892,7 @@ class Item_func_between :public Item_func_opt_neg protected: SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Field *field, Item *value); + bool val_int_cmp_int_finalize(longlong value, longlong a, longlong b); public: String value0,value1,value2; Item_func_between(THD *thd, Item *a, Item *b, Item *c): @@ -931,7 +933,8 @@ public: { return get_item_copy<Item_func_between>(thd, this); } longlong val_int_cmp_string(); - longlong val_int_cmp_temporal(); + longlong val_int_cmp_datetime(); + longlong val_int_cmp_time(); longlong val_int_cmp_int(); longlong val_int_cmp_real(); longlong val_int_cmp_decimal(); @@ -954,7 +957,7 @@ public: { if (agg_arg_charsets_for_comparison(cmp_collation, args, 2)) return TRUE; - fix_char_length(2); + fix_char_length(2); // returns "1" or "0" or "-1" return FALSE; } Item *get_copy(THD *thd) @@ -1008,8 +1011,8 @@ public: longlong int_op(); String *str_op(String *); my_decimal *decimal_op(my_decimal *); - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool time_op(MYSQL_TIME *ltime); + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool time_op(THD *thd, MYSQL_TIME *ltime); bool fix_length_and_dec() { if (aggregate_for_result(func_name(), args, arg_count, true)) @@ -1087,8 +1090,8 @@ public: longlong int_op(); String *str_op(String *str); my_decimal *decimal_op(my_decimal *); - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool time_op(MYSQL_TIME *ltime); + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool time_op(THD *thd, MYSQL_TIME *ltime); bool fix_length_and_dec() { if (Item_func_case_abbreviation2::fix_length_and_dec2(args)) @@ -1122,12 +1125,12 @@ public: :Item_func_case_abbreviation2(thd, a, b, c) { } - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - Datetime dt(current_thd, find_item(), fuzzydate); + Datetime dt(thd, find_item(), fuzzydate); return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } - bool time_op(MYSQL_TIME *ltime) + bool time_op(THD *thd, MYSQL_TIME *ltime) { return (null_value= Time(find_item()).copy_to_mysql_time(ltime)); } @@ -1240,8 +1243,8 @@ public: Item_func_hybrid_field_type::cleanup(); arg_count= 2; // See the comment to the constructor } - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool time_op(MYSQL_TIME *ltime); + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool time_op(THD *thd, MYSQL_TIME *ltime); double real_op(); longlong int_op(); String *str_op(String *str); @@ -1282,7 +1285,11 @@ public: { reset_first_arg_if_needed(); return this; } Item *derived_field_transformer_for_where(THD *thd, uchar *arg) { reset_first_arg_if_needed(); return this; } - Item *derived_grouping_field_transformer_for_where(THD *thd, uchar *arg) + Item *grouping_field_transformer_for_where(THD *thd, uchar *arg) + { reset_first_arg_if_needed(); return this; } + Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg) + { reset_first_arg_if_needed(); return this; } + Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg) { reset_first_arg_if_needed(); return this; } }; @@ -1410,8 +1417,6 @@ public: */ class in_temporal :public in_longlong { -protected: - uchar *get_value_internal(Item *item, enum_field_types f_type); public: /* Cache for the left item. */ @@ -1424,8 +1429,6 @@ public: Item_datetime *dt= static_cast<Item_datetime*>(item); dt->set(val->val, type_handler()->mysql_timestamp_type()); } - uchar *get_value(Item *item) - { return get_value_internal(item, type_handler()->field_type()); } friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b); }; @@ -1437,6 +1440,7 @@ public: :in_temporal(thd, elements) {} void set(uint pos,Item *item); + uchar *get_value(Item *item); const Type_handler *type_handler() const { return &type_handler_datetime2; } }; @@ -1448,6 +1452,7 @@ public: :in_temporal(thd, elements) {} void set(uint pos,Item *item); + uchar *get_value(Item *item); const Type_handler *type_handler() const { return &type_handler_time2; } }; @@ -1622,7 +1627,6 @@ class cmp_item_temporal: public cmp_item_scalar { protected: longlong value; - void store_value_internal(Item *item, enum_field_types type); public: cmp_item_temporal() {} int compare(cmp_item *ci); @@ -1637,7 +1641,8 @@ public: { } void store_value(Item *item) { - store_value_internal(item, MYSQL_TYPE_DATETIME); + value= item->val_datetime_packed(current_thd); + m_null_value= item->null_value; } int cmp_not_null(const Value *val); int cmp(Item *arg); @@ -1653,7 +1658,8 @@ public: { } void store_value(Item *item) { - store_value_internal(item, MYSQL_TYPE_TIME); + value= item->val_time_packed(current_thd); + m_null_value= item->null_value; } int cmp_not_null(const Value *val); int cmp(Item *arg); @@ -1891,7 +1897,7 @@ class Predicant_to_list_comparator return UNKNOWN; return in_item->cmp(args->arguments()[m_comparators[i].m_arg_index]); } - int cmp_args_nulls_equal(Item_args *args, uint i) + int cmp_args_nulls_equal(THD *thd, Item_args *args, uint i) { Predicant_to_value_comparator *cmp= &m_comparators[m_comparators[i].m_handler_index]; @@ -1902,7 +1908,7 @@ class Predicant_to_list_comparator ValueBuffer<MAX_FIELD_WIDTH> val; if (m_comparators[i].m_handler_index == i) in_item->store_value(predicant); - m_comparators[i].m_handler->Item_save_in_value(arg, &val); + m_comparators[i].m_handler->Item_save_in_value(thd, arg, &val); if (predicant->null_value && val.is_null()) return FALSE; // Two nulls are equal if (predicant->null_value || val.is_null()) @@ -2083,12 +2089,12 @@ public: /* Same as above, but treats two NULLs as equal, e.g. as in DECODE_ORACLE(). */ - bool cmp_nulls_equal(Item_args *args, uint *idx) + bool cmp_nulls_equal(THD *thd, Item_args *args, uint *idx) { for (uint i= 0 ; i < m_comparator_count ; i++) { DBUG_ASSERT(m_comparators[i].m_handler != NULL); - if (cmp_args_nulls_equal(args, i) == FALSE) + if (cmp_args_nulls_equal(thd, args, i) == FALSE) { *idx= m_comparators[i].m_arg_index; return false; // Found a matching value @@ -2124,8 +2130,8 @@ public: longlong int_op(); String *str_op(String *); my_decimal *decimal_op(my_decimal *); - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool time_op(MYSQL_TIME *ltime); + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool time_op(THD *thd, MYSQL_TIME *ltime); bool fix_fields(THD *thd, Item **ref); table_map not_null_tables() const { return 0; } const char *func_name() const { return "case"; } @@ -2421,12 +2427,19 @@ class cmp_item_row :public cmp_item { cmp_item **comparators; uint n; + bool alloc_comparators(THD *thd, uint n); + bool aggregate_row_elements_for_comparison(THD *thd, + Type_handler_hybrid_field_type *cmp, + Item_args *tmp, + const char *funcname, + uint col, + uint level); public: cmp_item_row(): comparators(0), n(0) {} ~cmp_item_row(); void store_value(Item *item); - bool alloc_comparators(THD *thd, uint n); - bool prepare_comparators(THD *, Item **args, uint arg_count); + bool prepare_comparators(THD *, const char *funcname, + const Item_args *args, uint level); int cmp(Item *arg); int cmp_not_null(const Value *val) { @@ -2507,9 +2520,8 @@ public: { Field *field=((Item_field*) args[0]->real_item())->field; - if (((field->type() == MYSQL_TYPE_DATE) || - (field->type() == MYSQL_TYPE_DATETIME)) && - (field->flags & NOT_NULL_FLAG)) + if ((field->flags & NOT_NULL_FLAG) && + field->type_handler()->cond_notnull_field_isnull_to_field_eq_zero()) return true; } return false; @@ -3084,7 +3096,6 @@ class Item_equal: public Item_bool_func const Type_handler *m_compare_handler; CHARSET_INFO *m_compare_collation; - String cmp_value1, cmp_value2; public: COND_EQUAL *upper_levels; /* multiple equalities of upper and levels */ @@ -3142,6 +3153,8 @@ public: { return used_tables() & tab_map; } + bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred); + friend class Item_equal_fields_iterator; bool count_sargable_conds(void *arg); friend class Item_equal_iterator<List_iterator_fast,Item>; @@ -3293,11 +3306,8 @@ public: inline bool is_cond_and(Item *item) { - if (item->type() != Item::COND_ITEM) - return FALSE; - - Item_cond *cond_item= (Item_cond*) item; - return (cond_item->functype() == Item_func::COND_AND_FUNC); + Item_func *func_item= item->get_item_func(); + return func_item && func_item->functype() == Item_func::COND_AND_FUNC; } class Item_cond_or :public Item_cond @@ -3398,11 +3408,8 @@ public: inline bool is_cond_or(Item *item) { - if (item->type() != Item::COND_ITEM) - return FALSE; - - Item_cond *cond_item= (Item_cond*) item; - return (cond_item->functype() == Item_func::COND_OR_FUNC); + Item_func *func_item= item->get_item_func(); + return func_item && func_item->functype() == Item_func::COND_OR_FUNC; } Item *and_expressions(Item *a, Item *b, Item **org_item); diff --git a/sql/item_create.cc b/sql/item_create.cc index ab91e378be3..87bf69f3c96 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -3487,12 +3487,11 @@ Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name, sph->add_used_routine(lex, thd, qname); if (pkgname.m_name.length) sp_handler_package_body.add_used_routine(lex, thd, &pkgname); + Name_resolution_context *ctx= lex->current_context(); if (arg_count > 0) - func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), - qname, sph, *item_list); + func= new (thd->mem_root) Item_func_sp(thd, ctx, qname, sph, *item_list); else - func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), - qname, sph); + func= new (thd->mem_root) Item_func_sp(thd, ctx, qname, sph); lex->safe_to_cache_query= 0; return func; @@ -3637,7 +3636,7 @@ Create_func_addtime Create_func_addtime::s_singleton; Item* Create_func_addtime::create_2_arg(THD *thd, Item *arg1, Item *arg2) { - return new (thd->mem_root) Item_func_add_time(thd, arg1, arg2, 0, 0); + return new (thd->mem_root) Item_func_add_time(thd, arg1, arg2, false); } @@ -6104,7 +6103,26 @@ Create_func_name_const Create_func_name_const::s_singleton; Item* Create_func_name_const::create_2_arg(THD *thd, Item *arg1, Item *arg2) { - return new (thd->mem_root) Item_name_const(thd, arg1, arg2); + if (!arg1->basic_const_item()) + goto err; + + if (arg2->basic_const_item()) + return new (thd->mem_root) Item_name_const(thd, arg1, arg2); + + if (arg2->type() == Item::FUNC_ITEM) + { + Item_func *value_func= (Item_func *) arg2; + if (value_func->functype() != Item_func::COLLATE_FUNC && + value_func->functype() != Item_func::NEG_FUNC) + goto err; + + if (!value_func->key_item()->basic_const_item()) + goto err; + return new (thd->mem_root) Item_name_const(thd, arg1, arg2); + } +err: + my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST"); + return NULL; } @@ -6658,7 +6676,7 @@ Create_func_subtime Create_func_subtime::s_singleton; Item* Create_func_subtime::create_2_arg(THD *thd, Item *arg1, Item *arg2) { - return new (thd->mem_root) Item_func_add_time(thd, arg1, arg2, 0, 1); + return new (thd->mem_root) Item_func_add_time(thd, arg1, arg2, true); } @@ -7433,84 +7451,6 @@ find_qualified_function_builder(THD *thd) } -static bool -have_important_literal_warnings(const MYSQL_TIME_STATUS *status) -{ - return (status->warnings & ~MYSQL_TIME_NOTE_TRUNCATED) != 0; -} - - -/** - Builder for datetime literals: - TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'. - @param thd The current thread - @param str Character literal - @param length Length of str - @param type Type of literal (TIME, DATE or DATETIME) - @param send_error Whether to generate an error on failure -*/ - -Item *create_temporal_literal(THD *thd, - const char *str, size_t length, - CHARSET_INFO *cs, - enum_field_types type, - bool send_error) -{ - MYSQL_TIME_STATUS status; - MYSQL_TIME ltime; - Item *item= NULL; - sql_mode_t flags= sql_mode_for_dates(thd); - - switch(type) - { - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_NEWDATE: - if (!str_to_datetime(cs, str, length, <ime, flags, &status) && - ltime.time_type == MYSQL_TIMESTAMP_DATE && !status.warnings) - item= new (thd->mem_root) Item_date_literal(thd, <ime); - break; - case MYSQL_TYPE_DATETIME: - if (!str_to_datetime(cs, str, length, <ime, flags, &status) && - ltime.time_type == MYSQL_TIMESTAMP_DATETIME && - !have_important_literal_warnings(&status)) - item= new (thd->mem_root) Item_datetime_literal(thd, <ime, - status.precision); - break; - case MYSQL_TYPE_TIME: - if (!str_to_time(cs, str, length, <ime, 0, &status) && - ltime.time_type == MYSQL_TIMESTAMP_TIME && - !have_important_literal_warnings(&status)) - item= new (thd->mem_root) Item_time_literal(thd, <ime, - status.precision); - break; - default: - DBUG_ASSERT(0); - } - - if (likely(item)) - { - if (status.warnings) // e.g. a note on nanosecond truncation - { - ErrConvString err(str, length, cs); - make_truncated_value_warning(thd, - Sql_condition::time_warn_level(status.warnings), - &err, ltime.time_type, 0); - } - return item; - } - - if (send_error) - { - const char *typestr= - (type == MYSQL_TYPE_DATE) ? "DATE" : - (type == MYSQL_TYPE_TIME) ? "TIME" : "DATETIME"; - ErrConvString err(str, length, thd->variables.character_set_client); - my_error(ER_WRONG_VALUE, MYF(0), typestr, err.ptr()); - } - return NULL; -} - - static List<Item> *create_func_dyncol_prepare(THD *thd, DYNCALL_CREATE_DEF **dfs, List<DYNCALL_CREATE_DEF> &list) diff --git a/sql/item_create.h b/sql/item_create.h index 5983a092cdc..4fb3c07c4ae 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -191,21 +191,6 @@ protected: #endif -Item *create_temporal_literal(THD *thd, - const char *str, size_t length, - CHARSET_INFO *cs, - enum_field_types type, - bool send_error); -inline -Item *create_temporal_literal(THD *thd, const String *str, - enum_field_types type, - bool send_error) -{ - return create_temporal_literal(thd, - str->ptr(), str->length(), str->charset(), - type, send_error); -} - struct Native_func_registry { LEX_CSTRING name; diff --git a/sql/item_func.cc b/sql/item_func.cc index c5ac97f2905..20c0a2564bb 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -145,7 +145,7 @@ void Item_func::sync_with_sum_func_and_with_field(List<Item> &list) Item *item; while ((item= li++)) { - with_sum_func|= item->with_sum_func; + join_with_sum_func(item); with_window_func|= item->with_window_func; with_field|= item->with_field; with_param|= item->with_param; @@ -367,7 +367,7 @@ Item_func::fix_fields(THD *thd, Item **ref) if (item->maybe_null) maybe_null=1; - with_sum_func= with_sum_func || item->with_sum_func; + join_with_sum_func(item); with_param= with_param || item->with_param; with_window_func= with_window_func || item->with_window_func; with_field= with_field || item->with_field; @@ -391,7 +391,7 @@ Item_func::quick_fix_field() { for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++) { - if (!(*arg)->fixed) + if (!(*arg)->is_fixed()) (*arg)->quick_fix_field(); } } @@ -734,7 +734,7 @@ void Item_func::signal_divide_by_null() Item *Item_func::get_tmp_table_item(THD *thd) { - if (!with_sum_func && !const_item()) + if (!Item_func::with_sum_func() && !const_item()) return new (thd->mem_root) Item_temptable_field(thd, result_field); return copy_or_same(thd); } @@ -806,50 +806,6 @@ bool Item_func_plus::fix_length_and_dec(void) } -String *Item_func_hybrid_field_type::val_str_from_decimal_op(String *str) -{ - my_decimal decimal_value, *val; - if (!(val= decimal_op_with_null_check(&decimal_value))) - return 0; // null is set - DBUG_ASSERT(!null_value); - my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val); - str->set_charset(collation.collation); - my_decimal2string(E_DEC_FATAL_ERROR, val, 0, 0, 0, str); - return str; -} - -double Item_func_hybrid_field_type::val_real_from_decimal_op() -{ - my_decimal decimal_value, *val; - if (!(val= decimal_op_with_null_check(&decimal_value))) - return 0.0; // null is set - double result; - my_decimal2double(E_DEC_FATAL_ERROR, val, &result); - return result; -} - -longlong Item_func_hybrid_field_type::val_int_from_decimal_op() -{ - my_decimal decimal_value, *val; - if (!(val= decimal_op_with_null_check(&decimal_value))) - return 0; // null is set - longlong result; - my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result); - return result; -} - -bool Item_func_hybrid_field_type::get_date_from_decimal_op(MYSQL_TIME *ltime, - ulonglong fuzzydate) -{ - my_decimal value, *res; - if (!(res= decimal_op_with_null_check(&value)) || - decimal_to_datetime_with_warn(res, ltime, fuzzydate, - field_name_or_null())) - return make_zero_mysql_time(ltime, fuzzydate); - return (null_value= 0); -} - - String *Item_func_hybrid_field_type::val_str_from_int_op(String *str) { longlong nr= int_op(); @@ -875,14 +831,13 @@ Item_func_hybrid_field_type::val_decimal_from_int_op(my_decimal *dec) return dec; } -bool Item_func_hybrid_field_type::get_date_from_int_op(MYSQL_TIME *ltime, - ulonglong fuzzydate) +bool Item_func_hybrid_field_type::get_date_from_int_op(THD *thd, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) { - longlong value= int_op(); - bool neg= !unsigned_flag && value < 0; - if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value, - ltime, fuzzydate, - field_name_or_null())) + Longlong_hybrid value(int_op(), unsigned_flag); + if (null_value || int_to_datetime_with_warn(thd, value, + ltime, fuzzydate, NULL)) return make_zero_mysql_time(ltime, fuzzydate); return (null_value= 0); } @@ -912,12 +867,13 @@ Item_func_hybrid_field_type::val_decimal_from_real_op(my_decimal *dec) return dec; } -bool Item_func_hybrid_field_type::get_date_from_real_op(MYSQL_TIME *ltime, - ulonglong fuzzydate) +bool Item_func_hybrid_field_type::get_date_from_real_op(THD *thd, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) { double value= real_op(); - if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, - field_name_or_null())) + if (null_value || + double_to_datetime_with_warn(thd, value, ltime, fuzzydate, NULL)) return make_zero_mysql_time(ltime, fuzzydate); return (null_value= 0); } @@ -926,7 +882,7 @@ bool Item_func_hybrid_field_type::get_date_from_real_op(MYSQL_TIME *ltime, String *Item_func_hybrid_field_type::val_str_from_date_op(String *str) { MYSQL_TIME ltime; - if (date_op_with_null_check(<ime) || + if (date_op_with_null_check(current_thd, <ime) || (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH))) return (String *) 0; str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); @@ -938,7 +894,7 @@ String *Item_func_hybrid_field_type::val_str_from_date_op(String *str) double Item_func_hybrid_field_type::val_real_from_date_op() { MYSQL_TIME ltime; - if (date_op_with_null_check(<ime)) + if (date_op_with_null_check(current_thd, <ime)) return 0; return TIME_to_double(<ime); } @@ -946,7 +902,7 @@ double Item_func_hybrid_field_type::val_real_from_date_op() longlong Item_func_hybrid_field_type::val_int_from_date_op() { MYSQL_TIME ltime; - if (date_op_with_null_check(<ime)) + if (date_op_with_null_check(current_thd, <ime)) return 0; return TIME_to_ulonglong(<ime); } @@ -955,7 +911,7 @@ my_decimal * Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec) { MYSQL_TIME ltime; - if (date_op_with_null_check(<ime)) + if (date_op_with_null_check(current_thd, <ime)) { my_decimal_set_zero(dec); return 0; @@ -967,7 +923,7 @@ Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec) String *Item_func_hybrid_field_type::val_str_from_time_op(String *str) { MYSQL_TIME ltime; - if (time_op_with_null_check(<ime) || + if (time_op_with_null_check(current_thd, <ime) || (null_value= my_TIME_to_str(<ime, str, decimals))) return NULL; return str; @@ -976,20 +932,22 @@ String *Item_func_hybrid_field_type::val_str_from_time_op(String *str) double Item_func_hybrid_field_type::val_real_from_time_op() { MYSQL_TIME ltime; - return time_op_with_null_check(<ime) ? 0 : TIME_to_double(<ime); + return time_op_with_null_check(current_thd, <ime) ? 0 : + TIME_to_double(<ime); } longlong Item_func_hybrid_field_type::val_int_from_time_op() { MYSQL_TIME ltime; - return time_op_with_null_check(<ime) ? 0 : TIME_to_ulonglong(<ime); + return time_op_with_null_check(current_thd, <ime) ? 0 : + TIME_to_ulonglong(<ime); } my_decimal * Item_func_hybrid_field_type::val_decimal_from_time_op(my_decimal *dec) { MYSQL_TIME ltime; - if (time_op_with_null_check(<ime)) + if (time_op_with_null_check(current_thd, <ime)) { my_decimal_set_zero(dec); return 0; @@ -1017,13 +975,14 @@ Item_func_hybrid_field_type::val_decimal_from_str_op(my_decimal *decimal_value) return res ? decimal_from_string_with_check(decimal_value, res) : 0; } -bool Item_func_hybrid_field_type::get_date_from_str_op(MYSQL_TIME *ltime, - ulonglong fuzzydate) +bool Item_func_hybrid_field_type::get_date_from_str_op(THD *thd, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) { StringBuffer<40> tmp; String *res; if (!(res= str_op_with_null_check(&tmp)) || - str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), + str_to_datetime_with_warn(thd, res->charset(), res->ptr(), res->length(), ltime, fuzzydate)) return make_zero_mysql_time(ltime, fuzzydate); return (null_value= 0); @@ -1048,47 +1007,15 @@ void Item_func_unsigned::print(String *str, enum_query_type query_type) } -String *Item_decimal_typecast::val_str(String *str) -{ - my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); - if (null_value) - return NULL; - my_decimal2string(E_DEC_FATAL_ERROR, tmp, 0, 0, 0, str); - return str; -} - - -double Item_decimal_typecast::val_real() -{ - my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); - double res; - if (null_value) - return 0.0; - my_decimal2double(E_DEC_FATAL_ERROR, tmp, &res); - return res; -} - - -longlong Item_decimal_typecast::val_int() -{ - my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); - longlong res; - if (null_value) - return 0; - my_decimal2int(E_DEC_FATAL_ERROR, tmp, unsigned_flag, &res); - return res; -} - - my_decimal *Item_decimal_typecast::val_decimal(my_decimal *dec) { - my_decimal tmp_buf, *tmp= args[0]->val_decimal(&tmp_buf); + VDec tmp(args[0]); bool sign; uint precision; - if ((null_value= args[0]->null_value)) + if ((null_value= tmp.is_null())) return NULL; - my_decimal_round(E_DEC_FATAL_ERROR, tmp, decimals, FALSE, dec); + tmp.round_to(dec, decimals, HALF_UP); sign= dec->sign(); if (unsigned_flag) { @@ -1272,17 +1199,13 @@ err: my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value) { - my_decimal value1, *val1; - my_decimal value2, *val2; - val1= args[0]->val_decimal(&value1); - if ((null_value= args[0]->null_value)) - return 0; - val2= args[1]->val_decimal(&value2); - if (!(null_value= (args[1]->null_value || + VDec2_lazy val(args[0], args[1]); + if (!(null_value= (val.has_null() || check_decimal_overflow(my_decimal_add(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, decimal_value, - val1, val2)) > 3))) + val.m_a.ptr(), + val.m_b.ptr())) > 3))) return decimal_value; return 0; } @@ -1412,18 +1335,13 @@ err: my_decimal *Item_func_minus::decimal_op(my_decimal *decimal_value) { - my_decimal value1, *val1; - my_decimal value2, *val2= - - val1= args[0]->val_decimal(&value1); - if ((null_value= args[0]->null_value)) - return 0; - val2= args[1]->val_decimal(&value2); - if (!(null_value= (args[1]->null_value || - (check_decimal_overflow(my_decimal_sub(E_DEC_FATAL_ERROR & - ~E_DEC_OVERFLOW, - decimal_value, val1, - val2)) > 3)))) + VDec2_lazy val(args[0], args[1]); + if (!(null_value= (val.has_null() || + check_decimal_overflow(my_decimal_sub(E_DEC_FATAL_ERROR & + ~E_DEC_OVERFLOW, + decimal_value, + val.m_a.ptr(), + val.m_b.ptr())) > 3))) return decimal_value; return 0; } @@ -1522,17 +1440,13 @@ err: my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value) { - my_decimal value1, *val1; - my_decimal value2, *val2; - val1= args[0]->val_decimal(&value1); - if ((null_value= args[0]->null_value)) - return 0; - val2= args[1]->val_decimal(&value2); - if (!(null_value= (args[1]->null_value || - (check_decimal_overflow(my_decimal_mul(E_DEC_FATAL_ERROR & - ~E_DEC_OVERFLOW, - decimal_value, val1, - val2)) > 3)))) + VDec2_lazy val(args[0], args[1]); + if (!(null_value= (val.has_null() || + check_decimal_overflow(my_decimal_mul(E_DEC_FATAL_ERROR & + ~E_DEC_OVERFLOW, + decimal_value, + val.m_a.ptr(), + val.m_b.ptr())) > 3))) return decimal_value; return 0; } @@ -1583,21 +1497,15 @@ double Item_func_div::real_op() my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) { - my_decimal value1, *val1; - my_decimal value2, *val2; int err; - - val1= args[0]->val_decimal(&value1); - if ((null_value= args[0]->null_value)) - return 0; - val2= args[1]->val_decimal(&value2); - if ((null_value= args[1]->null_value)) + VDec2_lazy val(args[0], args[1]); + if ((null_value= val.has_null())) return 0; if ((err= check_decimal_overflow(my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW & ~E_DEC_DIV_ZERO, decimal_value, - val1, val2, + val.m_a.ptr(), val.m_b.ptr(), prec_increment))) > 3) { if (err == E_DEC_DIV_ZERO) @@ -1687,20 +1595,14 @@ longlong Item_func_int_div::val_int() if (args[0]->result_type() != INT_RESULT || args[1]->result_type() != INT_RESULT) { - my_decimal tmp; - my_decimal *val0p= args[0]->val_decimal(&tmp); - if ((null_value= args[0]->null_value)) + VDec2_lazy val(args[0], args[1]); + if ((null_value= val.has_null())) return 0; - my_decimal val0= *val0p; - - my_decimal *val1p= args[1]->val_decimal(&tmp); - if ((null_value= args[1]->null_value)) - return 0; - my_decimal val1= *val1p; int err; + my_decimal tmp; if ((err= my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, &tmp, - &val0, &val1, 0)) > 3) + val.m_a.ptr(), val.m_b.ptr(), 0)) > 3) { if (err == E_DEC_DIV_ZERO) signal_divide_by_null(); @@ -1708,8 +1610,7 @@ longlong Item_func_int_div::val_int() } my_decimal truncated; - const bool do_truncate= true; - if (my_decimal_round(E_DEC_FATAL_ERROR, &tmp, 0, do_truncate, &truncated)) + if (tmp.round_to(&truncated, 0, TRUNCATE)) DBUG_ASSERT(false); longlong res; @@ -1811,17 +1712,11 @@ double Item_func_mod::real_op() my_decimal *Item_func_mod::decimal_op(my_decimal *decimal_value) { - my_decimal value1, *val1; - my_decimal value2, *val2; - - val1= args[0]->val_decimal(&value1); - if ((null_value= args[0]->null_value)) - return 0; - val2= args[1]->val_decimal(&value2); - if ((null_value= args[1]->null_value)) + VDec2_lazy val(args[0], args[1]); + if ((null_value= val.has_null())) return 0; switch (my_decimal_mod(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value, - val1, val2)) { + val.m_a.ptr(), val.m_b.ptr())) { case E_DEC_TRUNCATED: case E_DEC_OK: return decimal_value; @@ -1891,10 +1786,10 @@ longlong Item_func_neg::int_op() my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value) { - my_decimal val, *value= args[0]->val_decimal(&val); - if (!(null_value= args[0]->null_value)) + VDec value(args[0]); + if (!(null_value= value.is_null())) { - my_decimal2decimal(value, decimal_value); + my_decimal2decimal(value.ptr(), decimal_value); my_decimal_neg(decimal_value); return decimal_value; } @@ -1918,7 +1813,7 @@ void Item_func_neg::fix_length_and_dec_int() longlong val= args[0]->val_int(); if ((ulonglong) val >= (ulonglong) LONGLONG_MIN && ((ulonglong) val != (ulonglong) LONGLONG_MIN || - args[0]->type() != INT_ITEM)) + !args[0]->is_of_type(CONST_ITEM, INT_RESULT))) { /* Ensure that result is converted to DECIMAL, as longlong can't hold @@ -1989,10 +1884,10 @@ longlong Item_func_abs::int_op() my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value) { - my_decimal val, *value= args[0]->val_decimal(&val); - if (!(null_value= args[0]->null_value)) + VDec value(args[0]); + if (!(null_value= value.is_null())) { - my_decimal2decimal(value, decimal_value); + my_decimal2decimal(value.ptr(), decimal_value); if (decimal_value->sign()) my_decimal_neg(decimal_value); return decimal_value; @@ -2314,25 +2209,15 @@ bool Item_func_int_val::fix_length_and_dec() longlong Item_func_ceiling::int_op() { - longlong result; switch (args[0]->result_type()) { case INT_RESULT: - result= args[0]->val_int(); - null_value= args[0]->null_value; - break; + return val_int_from_item(args[0]); case DECIMAL_RESULT: - { - my_decimal dec_buf, *dec; - if ((dec= Item_func_ceiling::decimal_op(&dec_buf))) - my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result); - else - result= 0; + return VDec_op(this).to_longlong(unsigned_flag); + default: break; } - default: - result= (longlong)Item_func_ceiling::real_op(); - }; - return result; + return (longlong) Item_func_ceiling::real_op(); } @@ -2350,10 +2235,9 @@ double Item_func_ceiling::real_op() my_decimal *Item_func_ceiling::decimal_op(my_decimal *decimal_value) { - my_decimal val, *value= args[0]->val_decimal(&val); - if (!(null_value= (args[0]->null_value || - my_decimal_ceiling(E_DEC_FATAL_ERROR, value, - decimal_value) > 1))) + VDec value(args[0]); + if (!(null_value= (value.is_null() || + value.round_to(decimal_value, 0, CEILING) > 1))) return decimal_value; return 0; } @@ -2361,25 +2245,19 @@ my_decimal *Item_func_ceiling::decimal_op(my_decimal *decimal_value) longlong Item_func_floor::int_op() { - longlong result; switch (args[0]->result_type()) { case INT_RESULT: - result= args[0]->val_int(); - null_value= args[0]->null_value; - break; + return val_int_from_item(args[0]); case DECIMAL_RESULT: { my_decimal dec_buf, *dec; - if ((dec= Item_func_floor::decimal_op(&dec_buf))) - my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result); - else - result= 0; - break; + return (!(dec= Item_func_floor::decimal_op(&dec_buf))) ? 0 : + dec->to_longlong(unsigned_flag); } default: - result= (longlong)Item_func_floor::real_op(); - }; - return result; + break; + } + return (longlong) Item_func_floor::real_op(); } @@ -2397,10 +2275,9 @@ double Item_func_floor::real_op() my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value) { - my_decimal val, *value= args[0]->val_decimal(&val); - if (!(null_value= (args[0]->null_value || - my_decimal_floor(E_DEC_FATAL_ERROR, value, - decimal_value) > 1))) + VDec value(args[0]); + if (!(null_value= (value.is_null() || + value.round_to(decimal_value, 0, FLOOR) > 1))) return decimal_value; return 0; } @@ -2589,16 +2466,16 @@ longlong Item_func_round::int_op() my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value) { - my_decimal val, *value= args[0]->val_decimal(&val); + VDec value(args[0]); longlong dec= args[1]->val_int(); if (dec >= 0 || args[1]->unsigned_flag) dec= MY_MIN((ulonglong) dec, decimals); else if (dec < INT_MIN) dec= INT_MIN; - if (!(null_value= (args[0]->null_value || args[1]->null_value || - my_decimal_round(E_DEC_FATAL_ERROR, value, (int) dec, - truncate, decimal_value) > 1))) + if (!(null_value= (value.is_null() || args[1]->null_value || + value.round_to(decimal_value, (uint) dec, + truncate ? TRUNCATE : HALF_UP) > 1))) return decimal_value; return 0; } @@ -2736,14 +2613,15 @@ bool Item_func_min_max::fix_attributes(Item **items, uint nitems) 0 Otherwise */ -bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_min_max::get_date_native(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate) { longlong UNINIT_VAR(min_max); DBUG_ASSERT(fixed == 1); for (uint i=0; i < arg_count ; i++) { - longlong res= args[i]->val_datetime_packed(); + longlong res= args[i]->val_datetime_packed(thd); /* Check if we need to stop (because of error or KILL) and stop the loop */ if (unlikely(args[i]->null_value)) @@ -2754,8 +2632,8 @@ bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date) } unpack_time(min_max, ltime, mysql_timestamp_type()); - if (!(fuzzy_date & TIME_TIME_ONLY) && - unlikely((null_value= check_date_with_warn(ltime, fuzzy_date, + if (!(fuzzydate & TIME_TIME_ONLY) && + unlikely((null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)))) return true; @@ -2763,17 +2641,17 @@ bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date) } -bool Item_func_min_max::get_time_native(MYSQL_TIME *ltime) +bool Item_func_min_max::get_time_native(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); - Time value(args[0]); + Time value(thd, args[0], Time::Options(), decimals); if (!value.is_valid_time()) return (null_value= true); for (uint i= 1; i < arg_count ; i++) { - Time tmp(args[i]); + Time tmp(thd, args[i], Time::Options(), decimals); if (!tmp.is_valid_time()) return (null_value= true); @@ -3018,14 +2896,14 @@ longlong Item_func_field::val_int() } else if (cmp_type == DECIMAL_RESULT) { - my_decimal dec_arg_buf, *dec_arg, - dec_buf, *dec= args[0]->val_decimal(&dec_buf); - if (args[0]->null_value) + VDec dec(args[0]); + if (dec.is_null()) return 0; + my_decimal dec_arg_buf; for (uint i=1; i < arg_count; i++) { - dec_arg= args[i]->val_decimal(&dec_arg_buf); - if (!args[i]->null_value && !my_decimal_cmp(dec_arg, dec)) + my_decimal *dec_arg= args[i]->val_decimal(&dec_arg_buf); + if (!args[i]->null_value && !dec.cmp(dec_arg)) return (longlong) (i); } } @@ -3278,6 +3156,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, } uint i; Item **arg,**arg_end; + With_sum_func_cache *with_sum_func_cache= func->get_with_sum_func_cache(); for (i=0, arg=arguments, arg_end=arguments+arg_count; arg != arg_end ; arg++,i++) @@ -3301,7 +3180,8 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, func->collation.set(&my_charset_bin); if (item->maybe_null) func->maybe_null=1; - func->with_sum_func= func->with_sum_func || item->with_sum_func; + if (with_sum_func_cache) + with_sum_func_cache->join_with_sum_func(item); func->with_field= func->with_field || item->with_field; func->with_param= func->with_param || item->with_param; func->With_subquery_cache::join(item); @@ -3605,32 +3485,6 @@ String *Item_func_udf_int::val_str(String *str) } -longlong Item_func_udf_decimal::val_int() -{ - my_bool tmp_null_value; - longlong result; - my_decimal dec_buf, *dec= udf.val_decimal(&tmp_null_value, &dec_buf); - null_value= tmp_null_value; - if (null_value) - return 0; - my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result); - return result; -} - - -double Item_func_udf_decimal::val_real() -{ - my_bool tmp_null_value; - double result; - my_decimal dec_buf, *dec= udf.val_decimal(&tmp_null_value, &dec_buf); - null_value= tmp_null_value; - if (null_value) - return 0.0; - my_decimal2double(E_DEC_FATAL_ERROR, dec, &result); - return result; -} - - my_decimal *Item_func_udf_decimal::val_decimal(my_decimal *dec_buf) { my_decimal *res; @@ -3646,21 +3500,6 @@ my_decimal *Item_func_udf_decimal::val_decimal(my_decimal *dec_buf) } -String *Item_func_udf_decimal::val_str(String *str) -{ - my_bool tmp_null_value; - my_decimal dec_buf, *dec= udf.val_decimal(&tmp_null_value, &dec_buf); - null_value= tmp_null_value; - if (null_value) - return 0; - if (str->length() < DECIMAL_MAX_STR_LENGTH) - str->length(DECIMAL_MAX_STR_LENGTH); - my_decimal_round(E_DEC_FATAL_ERROR, dec, decimals, FALSE, &dec_buf); - my_decimal2string(E_DEC_FATAL_ERROR, &dec_buf, 0, 0, '0', str); - return str; -} - - /* Default max_length is max argument length */ bool Item_func_udf_str::fix_length_and_dec() @@ -3727,7 +3566,7 @@ longlong Item_master_pos_wait::val_int() connection_name.length= con->length(); if (check_master_connection_name(&connection_name)) { - my_error(ER_WRONG_ARGUMENTS, MYF(ME_JUST_WARNING), + my_error(ER_WRONG_ARGUMENTS, MYF(ME_WARNING), "MASTER_CONNECTION_NAME"); goto err; } @@ -4438,7 +4277,7 @@ user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name, if (!my_hash_inited(hash)) return 0; if (!(entry = (user_var_entry*) my_malloc(size, - MYF(MY_WME | ME_FATALERROR | + MYF(MY_WME | ME_FATAL | MY_THREAD_SPECIFIC)))) return 0; entry->name.str=(char*) entry+ ALIGN_SIZE(sizeof(user_var_entry))+ @@ -4693,7 +4532,7 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length, entry->value=0; entry->value= (char*) my_realloc(entry->value, length, MYF(MY_ALLOW_ZERO_PTR | MY_WME | - ME_FATALERROR | + ME_FATAL | MY_THREAD_SPECIFIC)); if (!entry->value) return 1; @@ -4758,11 +4597,7 @@ double user_var_entry::val_real(bool *null_value) case INT_RESULT: return (double) *(longlong*) value; case DECIMAL_RESULT: - { - double result; - my_decimal2double(E_DEC_FATAL_ERROR, (my_decimal *)value, &result); - return result; - } + return ((my_decimal *)value)->to_double(); case STRING_RESULT: return my_atof(value); // This is null terminated case ROW_RESULT: @@ -4787,11 +4622,7 @@ longlong user_var_entry::val_int(bool *null_value) const case INT_RESULT: return *(longlong*) value; case DECIMAL_RESULT: - { - longlong result; - my_decimal2int(E_DEC_FATAL_ERROR, (my_decimal *)value, 0, &result); - return result; - } + return ((my_decimal *)value)->to_longlong(false); case STRING_RESULT: { int error; @@ -5528,10 +5359,9 @@ bool Item_func_get_user_var::set_value(THD *thd, bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) { - DBUG_ASSERT(fixed == 0); + DBUG_ASSERT(!is_fixed()); DBUG_ASSERT(thd->lex->exchange); - if (Item::fix_fields(thd, ref) || - !(entry= get_variable(&thd->user_vars, &org_name, 1))) + if (!(entry= get_variable(&thd->user_vars, &org_name, 1))) return TRUE; entry->type= STRING_RESULT; /* @@ -5589,7 +5419,8 @@ my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer) } -bool Item_user_var_as_out_param::get_date(MYSQL_TIME *ltime, ulonglong fuzzy) +bool Item_user_var_as_out_param::get_date(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate) { DBUG_ASSERT(0); return true; @@ -5628,7 +5459,7 @@ void Item_func_get_system_var::update_null_value() THD *thd= current_thd; int save_no_errors= thd->no_errors; thd->no_errors= TRUE; - Item::update_null_value(); + type_handler()->Item_update_null_value(this); thd->no_errors= save_no_errors; } @@ -6468,7 +6299,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) (thd->lex->sql_command == SQLCOM_CREATE_VIEW)) { Security_context *save_security_ctx= thd->security_ctx; - if (context->security_ctx) + if (context && context->security_ctx) thd->security_ctx= context->security_ctx; /* @@ -6483,7 +6314,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) if (res) { - context->process_error(thd); + process_error(thd); DBUG_RETURN(res); } } @@ -6500,7 +6331,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) if (!(m_sp= sp)) { my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr()); - context->process_error(thd); + process_error(thd); DBUG_RETURN(TRUE); } @@ -6685,10 +6516,10 @@ my_decimal *Item_func_last_value::val_decimal(my_decimal *decimal_value) } -bool Item_func_last_value::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_func_last_value::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { evaluate_sideeffects(); - bool tmp= last_value->get_date(ltime, fuzzydate); + bool tmp= last_value->get_date(thd, ltime, fuzzydate); null_value= last_value->null_value; return tmp; } diff --git a/sql/item_func.h b/sql/item_func.h index fd789ffdc51..602b13fad7a 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -35,12 +35,11 @@ extern "C" /* Bug in BSDI include file */ #include <cmath> -class Item_func :public Item_func_or_sum +class Item_func :public Item_func_or_sum, + protected With_sum_func_cache { void sync_with_sum_func_and_with_field(List<Item> &list); protected: - String *val_str_from_val_str_ascii(String *str, String *str2); - virtual bool check_arguments() const { return check_argument_types_scalar(0, arg_count); @@ -56,6 +55,7 @@ protected: bool check_argument_types_can_return_text(uint start, uint end) const; bool check_argument_types_can_return_date(uint start, uint end) const; bool check_argument_types_can_return_time(uint start, uint end) const; + void print_cast_temporal(String *str, enum_query_type query_type); public: table_map not_null_tables_cache; @@ -80,49 +80,56 @@ public: CASE_SEARCHED_FUNC, // Used by ColumnStore/Spider CASE_SIMPLE_FUNC // Used by ColumnStore/spider }; + static scalar_comparison_op functype_to_scalar_comparison_op(Functype type) + { + switch (type) { + case EQ_FUNC: return SCALAR_CMP_EQ; + case EQUAL_FUNC: return SCALAR_CMP_EQUAL; + case LT_FUNC: return SCALAR_CMP_LT; + case LE_FUNC: return SCALAR_CMP_LE; + case GE_FUNC: return SCALAR_CMP_GE; + case GT_FUNC: return SCALAR_CMP_GT; + default: break; + } + DBUG_ASSERT(0); + return SCALAR_CMP_EQ; + } enum Type type() const { return FUNC_ITEM; } virtual enum Functype functype() const { return UNKNOWN_FUNC; } Item_func(THD *thd): Item_func_or_sum(thd) { - with_sum_func= 0; with_field= 0; with_param= 0; } - Item_func(THD *thd, Item *a): Item_func_or_sum(thd, a) + Item_func(THD *thd, Item *a) + :Item_func_or_sum(thd, a), With_sum_func_cache(a) { - with_sum_func= a->with_sum_func; with_param= a->with_param; with_field= a->with_field; } - Item_func(THD *thd, Item *a, Item *b): - Item_func_or_sum(thd, a, b) + Item_func(THD *thd, Item *a, Item *b) + :Item_func_or_sum(thd, a, b), With_sum_func_cache(a, b) { - with_sum_func= a->with_sum_func || b->with_sum_func; with_param= a->with_param || b->with_param; with_field= a->with_field || b->with_field; } - Item_func(THD *thd, Item *a, Item *b, Item *c): - Item_func_or_sum(thd, a, b, c) + Item_func(THD *thd, Item *a, Item *b, Item *c) + :Item_func_or_sum(thd, a, b, c), With_sum_func_cache(a, b, c) { - with_sum_func= a->with_sum_func || b->with_sum_func || c->with_sum_func; with_field= a->with_field || b->with_field || c->with_field; with_param= a->with_param || b->with_param || c->with_param; } - Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d): - Item_func_or_sum(thd, a, b, c, d) + Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d) + :Item_func_or_sum(thd, a, b, c, d), With_sum_func_cache(a, b, c, d) { - with_sum_func= a->with_sum_func || b->with_sum_func || - c->with_sum_func || d->with_sum_func; with_field= a->with_field || b->with_field || c->with_field || d->with_field; with_param= a->with_param || b->with_param || c->with_param || d->with_param; } - Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e): - Item_func_or_sum(thd, a, b, c, d, e) + Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e) + :Item_func_or_sum(thd, a, b, c, d, e), With_sum_func_cache(a, b, c, d, e) { - with_sum_func= a->with_sum_func || b->with_sum_func || - c->with_sum_func || d->with_sum_func || e->with_sum_func; with_field= a->with_field || b->with_field || c->with_field || d->with_field || e->with_field; with_param= a->with_param || b->with_param || @@ -134,11 +141,10 @@ public: set_arguments(thd, list); } // Constructor used for Item_cond_and/or (see Item comment) - Item_func(THD *thd, Item_func *item): - Item_func_or_sum(thd, item), + Item_func(THD *thd, Item_func *item) + :Item_func_or_sum(thd, item), With_sum_func_cache(item), not_null_tables_cache(item->not_null_tables_cache) - { - } + { } bool fix_fields(THD *, Item **ref); void cleanup() { @@ -174,16 +180,12 @@ public: virtual void print(String *str, enum_query_type query_type); void print_op(String *str, enum_query_type query_type); void print_args(String *str, uint from, enum_query_type query_type); - inline bool get_arg0_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) - { - DBUG_ASSERT(!(fuzzy_date & TIME_TIME_ONLY)); - Datetime dt(current_thd, args[0], fuzzy_date); - return (null_value= dt.copy_to_mysql_time(ltime)); - } bool is_null() { update_null_value(); return null_value; } + String *val_str_from_val_str_ascii(String *str, String *str2); + void signal_divide_by_null(); friend class udf_handler; Field *create_field_for_create_select(TABLE *table) @@ -338,6 +340,11 @@ public: return Item_args::excl_dep_on_grouping_fields(sel); } + bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) + { + return Item_args::excl_dep_on_in_subq_left_part(subq_pred); + } + /* We assume the result of any function that has a TIMESTAMP argument to be timezone-dependent, since a TIMESTAMP value in both numeric and string @@ -380,6 +387,10 @@ public: - or replaced to an Item_int_with_ref */ bool setup_args_and_comparator(THD *thd, Arg_comparator *cmp); + + bool with_sum_func() const { return m_with_sum_func; } + With_sum_func_cache* get_with_sum_func_cache() { return this; } + Item_func *get_item_func() { return this; } }; @@ -400,8 +411,8 @@ public: DBUG_ASSERT(fixed == 1); return Converter_double_to_longlong(val_real(), unsigned_flag).result(); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_real(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_real(thd, ltime, fuzzydate); } const Type_handler *type_handler() const { return &type_handler_double; } bool fix_length_and_dec() { @@ -441,6 +452,193 @@ public: }; +class Item_handled_func: public Item_func +{ +public: + class Handler + { + public: + virtual ~Handler() { } + virtual String *val_str(Item_handled_func *, String *) const= 0; + virtual String *val_str_ascii(Item_handled_func *, String *) const= 0; + virtual double val_real(Item_handled_func *) const= 0; + virtual longlong val_int(Item_handled_func *) const= 0; + virtual my_decimal *val_decimal(Item_handled_func *, my_decimal *) const= 0; + virtual bool get_date(THD *thd, Item_handled_func *, MYSQL_TIME *, date_mode_t fuzzydate) const= 0; + virtual const Type_handler *return_type_handler() const= 0; + virtual bool fix_length_and_dec(Item_handled_func *) const= 0; + }; + + /** + Abstract class for functions returning TIME, DATE, DATETIME or string values, + whose data type depends on parameters and is set at fix_fields time. + */ + class Handler_temporal: public Handler + { + public: + String *val_str(Item_handled_func *item, String *to) const + { + StringBuffer<MAX_FIELD_WIDTH> ascii_buf; + return item->val_str_from_val_str_ascii(to, &ascii_buf); + } + }; + + /** + Abstract class for functions returning strings, + which are generated from get_date() results, + when get_date() can return different MYSQL_TIMESTAMP_XXX per row. + */ + class Handler_temporal_string: public Handler_temporal + { + public: + const Type_handler *return_type_handler() const + { + return &type_handler_string; + } + double val_real(Item_handled_func *item) const + { + return Temporal_hybrid(item).to_double(); + } + longlong val_int(Item_handled_func *item) const + { + return Temporal_hybrid(item).to_longlong(); + } + my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const + { + return Temporal_hybrid(item).to_decimal(to); + } + String *val_str_ascii(Item_handled_func *item, String *to) const + { + return Temporal_hybrid(item).to_string(to, item->decimals); + } + }; + + + class Handler_date: public Handler_temporal + { + public: + const Type_handler *return_type_handler() const + { + return &type_handler_newdate; + } + bool fix_length_and_dec(Item_handled_func *item) const + { + item->fix_attributes_date(); + return false; + } + double val_real(Item_handled_func *item) const + { + return Date(item).to_double(); + } + longlong val_int(Item_handled_func *item) const + { + return Date(item).to_longlong(); + } + my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const + { + return Date(item).to_decimal(to); + } + String *val_str_ascii(Item_handled_func *item, String *to) const + { + return Date(item).to_string(to); + } + }; + + + class Handler_time: public Handler_temporal + { + public: + const Type_handler *return_type_handler() const + { + return &type_handler_time2; + } + double val_real(Item_handled_func *item) const + { + return Time(item).to_double(); + } + longlong val_int(Item_handled_func *item) const + { + return Time(item).to_longlong(); + } + my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const + { + return Time(item).to_decimal(to); + } + String *val_str_ascii(Item_handled_func *item, String *to) const + { + return Time(item).to_string(to, item->decimals); + } + }; + + + class Handler_datetime: public Handler_temporal + { + public: + const Type_handler *return_type_handler() const + { + return &type_handler_datetime2; + } + double val_real(Item_handled_func *item) const + { + return Datetime(item).to_double(); + } + longlong val_int(Item_handled_func *item) const + { + return Datetime(item).to_longlong(); + } + my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const + { + return Datetime(item).to_decimal(to); + } + String *val_str_ascii(Item_handled_func *item, String *to) const + { + return Datetime(item).to_string(to, item->decimals); + } + }; + + +protected: + const Handler *m_func_handler; +public: + Item_handled_func(THD *thd, Item *a) + :Item_func(thd, a), m_func_handler(NULL) { } + Item_handled_func(THD *thd, Item *a, Item *b) + :Item_func(thd, a, b), m_func_handler(NULL) { } + void set_func_handler(const Handler *handler) + { + m_func_handler= handler; + } + const Type_handler *type_handler() const + { + return m_func_handler->return_type_handler(); + } + String *val_str(String *to) + { + return m_func_handler->val_str(this, to); + } + String *val_str_ascii(String *to) + { + return m_func_handler->val_str_ascii(this, to); + } + double val_real() + { + return m_func_handler->val_real(this); + } + longlong val_int() + { + return m_func_handler->val_int(this); + } + my_decimal *val_decimal(my_decimal *to) + { + return m_func_handler->val_decimal(this, to); + } + bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) + { + return m_func_handler->get_date(thd, this, to, fuzzydate); + } +}; + + /** Functions that at fix_fields() time determine the returned field type, trying to preserve the exact data type of the arguments. @@ -463,15 +661,15 @@ class Item_func_hybrid_field_type: public Item_hybrid_func Helper methods to make sure that the result of decimal_op(), str_op() and date_op() is properly synched with null_value. */ - bool date_op_with_null_check(MYSQL_TIME *ltime) + bool date_op_with_null_check(THD *thd, MYSQL_TIME *ltime) { - bool rc= date_op(ltime, 0); + bool rc= date_op(thd, ltime, date_mode_t(0)); DBUG_ASSERT(!rc ^ null_value); return rc; } - bool time_op_with_null_check(MYSQL_TIME *ltime) + bool time_op_with_null_check(THD *thd, MYSQL_TIME *ltime) { - bool rc= time_op(ltime); + bool rc= time_op(thd, ltime); DBUG_ASSERT(!rc ^ null_value); DBUG_ASSERT(rc || ltime->time_type == MYSQL_TIMESTAMP_TIME); return rc; @@ -482,13 +680,7 @@ class Item_func_hybrid_field_type: public Item_hybrid_func DBUG_ASSERT((res != NULL) ^ null_value); return res; } - my_decimal *decimal_op_with_null_check(my_decimal *decimal_buffer) - { - my_decimal *res= decimal_op(decimal_buffer); - DBUG_ASSERT((res != NULL) ^ null_value); - return res; - } - bool make_zero_mysql_time(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool make_zero_mysql_time(MYSQL_TIME *ltime, date_mode_t fuzzydate) { bzero(ltime, sizeof(*ltime)); return null_value|= !(fuzzydate & TIME_FUZZY_DATES); @@ -500,10 +692,6 @@ public: { return str_op_with_null_check(&str_value); } - my_decimal *val_decimal_from_decimal_op(my_decimal *dec) - { - return decimal_op_with_null_check(dec); - } longlong val_int_from_int_op() { return int_op(); @@ -514,7 +702,6 @@ public: } // Value methods that involve conversion - String *val_str_from_decimal_op(String *str); String *val_str_from_real_op(String *str); String *val_str_from_int_op(String *str); String *val_str_from_date_op(String *str); @@ -528,20 +715,17 @@ public: longlong val_int_from_str_op(); longlong val_int_from_real_op(); - longlong val_int_from_decimal_op(); longlong val_int_from_date_op(); longlong val_int_from_time_op(); double val_real_from_str_op(); - double val_real_from_decimal_op(); double val_real_from_date_op(); double val_real_from_time_op(); double val_real_from_int_op(); - bool get_date_from_str_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_real_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_decimal_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_int_op(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date_from_str_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_date_from_real_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_date_from_int_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); public: Item_func_hybrid_field_type(THD *thd): @@ -586,11 +770,11 @@ public: DBUG_ASSERT(null_value == (res == NULL)); return res; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate) { DBUG_ASSERT(fixed); return Item_func_hybrid_field_type::type_handler()-> - Item_func_hybrid_field_type_get_date(this, res, fuzzy_date); + Item_func_hybrid_field_type_get_date(thd, this, res, fuzzydate); } /** @@ -600,6 +784,16 @@ public: @return The result of the operation. */ virtual longlong int_op()= 0; + Longlong_null to_longlong_null_op() + { + longlong nr= int_op(); + /* + C++ does not guarantee the order of parameter evaluation, + so to make sure "null_value" is passed to the constructor + after the int_op() call, int_op() is caled on a separate line. + */ + return Longlong_null(nr, null_value); + } /** @brief Performs the operation that this functions implements when the @@ -634,14 +828,14 @@ public: field type is DATETIME or DATE. @return The result of the operation. */ - virtual bool date_op(MYSQL_TIME *res, ulonglong fuzzy_date)= 0; + virtual bool date_op(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate)= 0; /** @brief Performs the operation that this functions implements when field type is TIME. @return The result of the operation. */ - virtual bool time_op(MYSQL_TIME *res)= 0; + virtual bool time_op(THD *thd, MYSQL_TIME *res)= 0; }; @@ -700,12 +894,12 @@ public: Item_func_hybrid_field_type(thd, list) { } String *str_op(String *str) { DBUG_ASSERT(0); return 0; } - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(0); return true; } - bool time_op(MYSQL_TIME *ltime) + bool time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(0); return true; @@ -791,8 +985,8 @@ public: { collation.set_numeric(); } double val_real(); String *val_str(String*str); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_int(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_int(thd, ltime, fuzzydate); } const Type_handler *type_handler() const= 0; bool fix_length_and_dec() { return FALSE; } }; @@ -982,12 +1176,12 @@ public: fix_char_length(my_decimal_precision_to_length_no_truncation(len, dec, unsigned_flag)); } - String *val_str(String *str); - double val_real(); - longlong val_int(); + String *val_str(String *str) { return VDec(this).to_string(str); } + double val_real() { return VDec(this).to_double(); } + longlong val_int() { return VDec(this).to_longlong(unsigned_flag); } my_decimal *val_decimal(my_decimal*); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_decimal(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return VDec(this).to_datetime_with_warn(thd, ltime, fuzzydate, this); } const Type_handler *type_handler() const { return &type_handler_newdecimal; } void fix_length_and_dec_generic() {} bool fix_length_and_dec() @@ -1541,8 +1735,8 @@ public: double val_real_native(); longlong val_int_native(); my_decimal *val_decimal_native(my_decimal *); - bool get_date_native(MYSQL_TIME *res, ulonglong fuzzydate); - bool get_time_native(MYSQL_TIME *res); + bool get_date_native(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); + bool get_time_native(THD *thd, MYSQL_TIME *res); double val_real() { @@ -1568,11 +1762,11 @@ public: return Item_func_min_max::type_handler()-> Item_func_min_max_val_decimal(this, dec); } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate) { DBUG_ASSERT(fixed); return Item_func_min_max::type_handler()-> - Item_func_min_max_get_date(this, res, fuzzy_date); + Item_func_min_max_get_date(thd, this, res, fuzzydate); } void aggregate_attributes_real(Item **items, uint nitems) { @@ -1639,8 +1833,8 @@ public: String *val_str(String *str) { return val_str_from_item(args[0], str); } my_decimal *val_decimal(my_decimal *dec) { return val_decimal_from_item(args[0], dec); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_item(args[0], ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_item(thd, args[0], ltime, fuzzydate); } const char *func_name() const { return "rollup_const"; } bool const_item() const { return 0; } const Type_handler *type_handler() const { return args[0]->type_handler(); } @@ -2007,6 +2201,18 @@ protected: udf_handler udf; bool is_expensive_processor(void *arg) { return TRUE; } + class VDec_udf: public Dec_ptr_and_buffer + { + public: + VDec_udf(Item_udf_func *func, udf_handler *udf) + { + my_bool tmp_null_value; + m_ptr= udf->val_decimal(&tmp_null_value, &m_buffer); + DBUG_ASSERT(is_null() == (tmp_null_value != 0)); + func->null_value= is_null(); + } + }; + public: Item_udf_func(THD *thd, udf_func *udf_arg): Item_func(thd), udf(udf_arg) {} @@ -2084,9 +2290,9 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } }; @@ -2147,10 +2353,19 @@ public: Item_udf_func(thd, udf_arg) {} Item_func_udf_decimal(THD *thd, udf_func *udf_arg, List<Item> &list): Item_udf_func(thd, udf_arg, list) {} - longlong val_int(); - double val_real(); + longlong val_int() + { + return VDec_udf(this, &udf).to_longlong(unsigned_flag); + } + double val_real() + { + return VDec_udf(this, &udf).to_double(); + } my_decimal *val_decimal(my_decimal *); - String *val_str(String *str); + String *val_str(String *str) + { + return VDec_udf(this, &udf).to_string_round(str, decimals); + } const Type_handler *type_handler() const { return &type_handler_newdecimal; } bool fix_length_and_dec() { fix_num_length_and_dec(); return FALSE; } Item *get_copy(THD *thd) @@ -2381,13 +2596,13 @@ public: Item_func_user_var(THD *thd, Item_func_user_var *item) :Item_hybrid_func(thd, item), m_var_entry(item->m_var_entry), name(item->name) { } - Field *create_tmp_field(bool group, TABLE *table) - { return create_table_field_from_handler(table); } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param); Field *create_field_for_create_select(TABLE *table) { return create_table_field_from_handler(table); } bool check_vcol_func_processor(void *arg); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return type_handler()->Item_get_date(this, ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } }; @@ -2515,14 +2730,14 @@ public: in List<Item> and desire to place this code somewhere near other functions working with user variables. */ -class Item_user_var_as_out_param :public Item, +class Item_user_var_as_out_param :public Item_fixed_hybrid, public Load_data_outvar { LEX_CSTRING org_name; user_var_entry *entry; public: Item_user_var_as_out_param(THD *thd, const LEX_CSTRING *a) - :Item(thd) + :Item_fixed_hybrid(thd) { DBUG_ASSERT(a->length < UINT_MAX32); org_name= *a; @@ -2557,12 +2772,18 @@ public: { return 0; } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + DBUG_ASSERT(0); + return NULL; + } /* We should return something different from FIELD_ITEM here */ - enum Type type() const { return STRING_ITEM;} + enum Type type() const { return CONST_ITEM;} double val_real(); longlong val_int(); String *val_str(String *str); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); my_decimal *val_decimal(my_decimal *decimal_buffer); /* fix_fields() binds variable name with its entry structure */ bool fix_fields(THD *thd, Item **ref); @@ -2609,9 +2830,9 @@ public: String* val_str(String*); my_decimal *val_decimal(my_decimal *dec_buf) { return val_decimal_from_real(dec_buf); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } /* TODO: fix to support views */ const char *func_name() const { return "get_system_var"; } @@ -2870,6 +3091,8 @@ public: const Type_handler *type_handler() const; + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param); Field *create_field_for_create_select(TABLE *table) { return result_type() != STRING_RESULT ? @@ -2899,7 +3122,7 @@ public: return sp_result_field->val_decimal(dec_buf); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (execute()) return true; @@ -3053,7 +3276,7 @@ public: longlong val_int(); String *val_str(String *); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool fix_length_and_dec(); const char *func_name() const { return "last_value"; } const Type_handler *type_handler() const { return last_value->type_handler(); } diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 4c2a2fa8b11..1f1b5a6ceed 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -916,7 +916,7 @@ String *Item_func_point::val_str(String *str) if ((null_value= (args[0]->null_value || args[1]->null_value || - str->realloc(4/*SRID*/ + 1 + 4 + SIZEOF_STORED_DOUBLE * 2)))) + str->alloc(4/*SRID*/ + 1 + 4 + SIZEOF_STORED_DOUBLE * 2)))) return 0; str->set_charset(&my_charset_bin); diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index 0e727829ce7..e6c198fb8b2 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -512,7 +512,7 @@ public: return TRUE; for (unsigned int i= 0; i < arg_count; ++i) { - if (args[i]->fixed && args[i]->field_type() != MYSQL_TYPE_GEOMETRY) + if (args[i]->is_fixed() && args[i]->field_type() != MYSQL_TYPE_GEOMETRY) { String str; args[i]->print(&str, QT_NO_DATA_EXPANSION); diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 230d954aa77..4ec481cf439 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -1422,7 +1422,7 @@ null_return: static int append_json_value(String *str, Item *item, String *tmp_val) { - if (item->is_bool_type()) + if (item->type_handler()->is_bool_type()) { longlong v_int= item->val_int(); const char *t_f; diff --git a/sql/item_row.cc b/sql/item_row.cc index 8233ba00f06..665c900cb3a 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -60,7 +60,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref) } } maybe_null|= item->maybe_null; - with_sum_func= with_sum_func || item->with_sum_func; + join_with_sum_func(item); with_window_func = with_window_func || item->with_window_func; with_field= with_field || item->with_field; m_with_subquery|= item->with_subquery(); @@ -91,7 +91,7 @@ void Item_row::cleanup() { DBUG_ENTER("Item_row::cleanup"); - Item::cleanup(); + Item_fixed_hybrid::cleanup(); /* Reset to the original values */ used_tables_and_const_cache_init(); with_null= 0; diff --git a/sql/item_row.h b/sql/item_row.h index e0d54403730..4f60a33ab9f 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -33,10 +33,11 @@ Item which stores (x,y,...) and ROW(x,y,...). Note that this can be recursive: ((x,y),(z,t)) is a ROW of ROWs. */ -class Item_row: public Item, +class Item_row: public Item_fixed_hybrid, private Item_args, private Used_tables_and_const_cache, - private With_subquery_cache + private With_subquery_cache, + private With_sum_func_cache { table_map not_null_tables_cache; /** @@ -45,17 +46,25 @@ class Item_row: public Item, */ bool with_null; public: - Item_row(THD *thd, List<Item> &list): - Item(thd), Item_args(thd, list), not_null_tables_cache(0), with_null(0) + Item_row(THD *thd, List<Item> &list) + :Item_fixed_hybrid(thd), Item_args(thd, list), + not_null_tables_cache(0), with_null(0) { } - Item_row(THD *thd, Item_row *row): - Item(thd), Item_args(thd, static_cast<Item_args*>(row)), Used_tables_and_const_cache(), + Item_row(THD *thd, Item_row *row) + :Item_fixed_hybrid(thd), Item_args(thd, static_cast<Item_args*>(row)), + Used_tables_and_const_cache(), + With_sum_func_cache(*row), not_null_tables_cache(0), with_null(0) { } bool with_subquery() const { DBUG_ASSERT(fixed); return m_with_subquery; } enum Type type() const { return ROW_ITEM; }; const Type_handler *type_handler() const { return &type_handler_row; } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + return NULL; // Check with Vicentiu why it's called for Item_row + } void illegal_method_call(const char *); bool is_null() { return null_value; } void make_send_field(THD *thd, Send_field *) @@ -82,7 +91,7 @@ public: illegal_method_call((const char*)"val_decimal"); return 0; }; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { illegal_method_call((const char*)"get_date"); return true; @@ -92,6 +101,8 @@ public: void cleanup(); void split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &fields, uint flags); + bool with_sum_func() const { return m_with_sum_func; } + With_sum_func_cache* get_with_sum_func_cache() { return this; } table_map used_tables() const { return used_tables_cache; }; bool const_item() const { return const_item_cache; }; void update_used_tables() @@ -134,6 +145,11 @@ public: return Item_args::excl_dep_on_grouping_fields(sel); } + bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) + { + return Item_args::excl_dep_on_in_subq_left_part(subq_pred); + } + bool check_vcol_func_processor(void *arg) {return FALSE; } Item *get_copy(THD *thd) { return get_item_copy<Item_row>(thd, this); } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index fda7f59b128..6c82c580858 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -255,7 +255,7 @@ String *Item_func_sha2::val_str_ascii(String *str) Since we're subverting the usual String methods, we must make sure that the destination has space for the bytes we're about to write. */ - str->realloc((uint) digest_length*2 + 1); /* Each byte as two nybbles */ + str->alloc((uint) digest_length*2 + 1); /* Each byte as two nybbles */ /* Convert the large number to a string-hex representation. */ array_to_hex((char *) str->ptr(), digest_buf, (uint)digest_length); @@ -762,7 +762,7 @@ String *Item_func_des_encrypt::val_str(String *str) tail= 8 - (res_length % 8); // 1..8 marking extra length res_length+=tail; - if (tmp_arg.realloc(res_length)) + if (tmp_arg.alloc(res_length)) goto error; tmp_arg.length(0); tmp_arg.append(res->ptr(), res->length()); @@ -770,7 +770,6 @@ String *Item_func_des_encrypt::val_str(String *str) if (tmp_arg.append(append_str, tail) || str->alloc(res_length+1)) goto error; tmp_arg[res_length-1]=tail; // save extra length - str->realloc(res_length+1); str->length(res_length+1); str->set_charset(&my_charset_bin); (*str)[0]=(char) (128 | key_number); @@ -1017,7 +1016,7 @@ String *Item_func_concat_ws::val_str(String *str) { uint new_len = MY_MAX(tmp_value.alloced_length() * 2, concat_len); - if (tmp_value.realloc(new_len)) + if (tmp_value.alloc(new_len)) goto null; } } @@ -1072,8 +1071,7 @@ String *Item_func_reverse::val_str(String *str) /* An empty string is a special case as the string pointer may be null */ if (!res->length()) return make_empty_result(); - if (str->alloced_length() < res->length() && - str->realloc(res->length())) + if (str->alloc(res->length())) { null_value= 1; return 0; @@ -2659,12 +2657,10 @@ String *Item_func_format::val_str_ascii(String *str) if (args[0]->result_type() == DECIMAL_RESULT || args[0]->result_type() == INT_RESULT) { - my_decimal dec_val, rnd_dec, *res; - res= args[0]->val_decimal(&dec_val); - if ((null_value=args[0]->null_value)) + VDec res(args[0]); + if ((null_value= res.is_null())) return 0; /* purecov: inspected */ - my_decimal_round(E_DEC_FATAL_ERROR, res, dec, false, &rnd_dec); - my_decimal2string(E_DEC_FATAL_ERROR, &rnd_dec, 0, 0, 0, str); + res.to_string_round(str, dec); str_length= str->length(); } else @@ -4180,7 +4176,7 @@ String *Item_func_compress::val_str(String *str) // Check new_size overflow: new_size <= res->length() if (((uint32) (new_size+5) <= res->length()) || - str->realloc((uint32) new_size + 4 + 1)) + str->alloc((uint32) new_size + 4 + 1)) { null_value= 1; return 0; @@ -4252,7 +4248,7 @@ String *Item_func_uncompress::val_str(String *str) max_allowed_packet)); goto err; } - if (str->realloc((uint32)new_size)) + if (str->alloc((uint32)new_size)) goto err; if ((err= uncompress((Byte*)str->ptr(), &new_size, @@ -4281,7 +4277,7 @@ String *Item_func_uuid::val_str(String *str) DBUG_ASSERT(fixed == 1); uchar guid[MY_UUID_SIZE]; - str->realloc(MY_UUID_STRING_LENGTH+1); + str->alloc(MY_UUID_STRING_LENGTH+1); str->length(MY_UUID_STRING_LENGTH); str->set_charset(system_charset_info); my_uuid(guid); @@ -4547,11 +4543,11 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg) break; case DYN_COL_DATETIME: case DYN_COL_DATE: - args[valpos]->get_date(&vals[i].x.time_value, + args[valpos]->get_date(thd, &vals[i].x.time_value, sql_mode_for_dates(thd)); break; case DYN_COL_TIME: - args[valpos]->get_time(&vals[i].x.time_value); + args[valpos]->get_time(thd, &vals[i].x.time_value); break; default: DBUG_ASSERT(0); @@ -5117,7 +5113,7 @@ null: } -bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_dyncol_get::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DYNAMIC_COLUMN_VALUE val; char buff[STRING_BUFFER_USUAL_SIZE]; @@ -5138,10 +5134,8 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (signed_value || val.x.ulong_value <= LONGLONG_MAX) { longlong llval = (longlong)val.x.ulong_value; - bool neg = llval < 0; - if (int_to_datetime_with_warn(neg, (ulonglong)(neg ? -llval : - llval), - ltime, fuzzy_date, 0 /* TODO */)) + if (int_to_datetime_with_warn(thd, Longlong_hybrid(llval, !signed_value), + ltime, fuzzydate, 0 /* TODO */)) goto null; return 0; } @@ -5149,20 +5143,20 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) val.x.double_value= static_cast<double>(ULONGLONG_MAX); /* fall through */ case DYN_COL_DOUBLE: - if (double_to_datetime_with_warn(val.x.double_value, ltime, fuzzy_date, + if (double_to_datetime_with_warn(thd, val.x.double_value, ltime, fuzzydate, 0 /* TODO */)) goto null; return 0; case DYN_COL_DECIMAL: - if (decimal_to_datetime_with_warn((my_decimal*)&val.x.decimal.value, ltime, - fuzzy_date, 0 /* TODO */)) + if (decimal_to_datetime_with_warn(thd, (my_decimal*)&val.x.decimal.value, + ltime, fuzzydate, 0 /* TODO */)) goto null; return 0; case DYN_COL_STRING: - if (str_to_datetime_with_warn(&my_charset_numeric, + if (str_to_datetime_with_warn(thd, &my_charset_numeric, val.x.string.value.str, val.x.string.value.length, - ltime, fuzzy_date)) + ltime, fuzzydate)) goto null; return 0; case DYN_COL_DATETIME: diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 29af0b43d9d..762a3c2559e 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -62,16 +62,11 @@ public: longlong val_int(); double val_real(); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_string(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_string(thd, ltime, fuzzydate); } const Type_handler *type_handler() const { return string_type_handler(); } void left_right_max_length(); bool fix_fields(THD *thd, Item **ref); - void update_null_value() - { - StringBuffer<MAX_FIELD_WIDTH> tmp; - (void) val_str(&tmp); - } }; @@ -1470,11 +1465,11 @@ public: return NULL; return res; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (args[0]->result_type() == STRING_RESULT) - return Item_str_func::get_date(ltime, fuzzydate); - bool res= args[0]->get_date(ltime, fuzzydate); + return Item_str_func::get_date(thd, ltime, fuzzydate); + bool res= args[0]->get_date(thd, ltime, fuzzydate); if ((null_value= args[0]->null_value)) return 1; return res; @@ -1769,7 +1764,7 @@ public: double val_real(); my_decimal *val_decimal(my_decimal *); bool get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val, String *tmp); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); void print(String *str, enum_query_type query_type); Item *get_copy(THD *thd) { return get_item_copy<Item_dyncol_get>(thd, this); } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 1947a45186a..9298c9998a8 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -85,6 +85,9 @@ void Item_subselect::init(st_select_lex *select_lex, DBUG_ENTER("Item_subselect::init"); DBUG_PRINT("enter", ("select_lex: %p this: %p", select_lex, this)); + + select_lex->parent_lex->relink_hack(select_lex); + unit= select_lex->master_unit(); if (unit->item) @@ -123,13 +126,6 @@ void Item_subselect::init(st_select_lex *select_lex, else engine= new subselect_single_select_engine(select_lex, result, this); } - { - SELECT_LEX *upper= unit->outer_select(); - if (upper->parsing_place == IN_HAVING) - upper->subquery_in_having= 1; - /* The subquery is an expression cache candidate */ - upper->expr_cache_may_be_used[upper->parsing_place]= TRUE; - } DBUG_PRINT("info", ("engine: %p", engine)); DBUG_VOID_RETURN; } @@ -220,7 +216,8 @@ Item_subselect::~Item_subselect() if (own_engine) delete engine; else - engine->cleanup(); + if (engine) // can be empty in case of EOM + engine->cleanup(); engine= NULL; DBUG_VOID_RETURN; } @@ -244,6 +241,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) DBUG_ASSERT(unit->thd == thd); + { + SELECT_LEX *upper= unit->outer_select(); + if (upper->parsing_place == IN_HAVING) + upper->subquery_in_having= 1; + /* The subquery is an expression cache candidate */ + upper->expr_cache_may_be_used[upper->parsing_place]= TRUE; + } + status_var_increment(thd_param->status_var.feature_subquery); DBUG_ASSERT(fixed == 0); @@ -939,7 +944,7 @@ bool Item_subselect::const_item() const Item *Item_subselect::get_tmp_table_item(THD *thd_arg) { - if (!with_sum_func && !const_item()) + if (!Item_subselect::with_sum_func() && !const_item()) return new (thd->mem_root) Item_temptable_field(thd_arg, result_field); return copy_or_same(thd_arg); } @@ -1142,7 +1147,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) if (!select_lex->master_unit()->is_unit_op() && !select_lex->table_list.elements && select_lex->item_list.elements == 1 && - !select_lex->item_list.head()->with_sum_func && + !select_lex->item_list.head()->with_sum_func() && /* We cant change name of Item_field or Item_ref, because it will prevent it's correct resolving, but we should save name of @@ -1383,15 +1388,15 @@ bool Item_singlerow_subselect::val_bool() } -bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_singlerow_subselect::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->get_date(ltime, fuzzydate); + return value->get_date(thd, ltime, fuzzydate); if (!exec() && !value->null_value) { null_value= FALSE; - return value->get_date(ltime, fuzzydate); + return value->get_date(thd, ltime, fuzzydate); } else { @@ -1407,6 +1412,8 @@ Item_exists_subselect::Item_exists_subselect(THD *thd, emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0) { DBUG_ENTER("Item_exists_subselect::Item_exists_subselect"); + + init(select_lex, new (thd->mem_root) select_exists_subselect(thd, this)); max_columns= UINT_MAX; null_value= FALSE; //can't be NULL @@ -1449,6 +1456,7 @@ Item_in_subselect::Item_in_subselect(THD *thd, Item * left_exp, { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); DBUG_PRINT("info", ("in_strategy: %u", (uint)in_strategy)); + left_expr_orig= left_expr= left_exp; /* prepare to possible disassembling the item in convert_subq_to_sj() */ if (left_exp->type() == Item::ROW_ITEM) @@ -2039,7 +2047,7 @@ bool Item_in_subselect::fix_having(Item *having, SELECT_LEX *select_lex) { bool fix_res= 0; DBUG_ASSERT(thd); - if (!having->fixed) + if (!having->is_fixed()) { select_lex->having_fix_field= 1; fix_res= having->fix_fields(thd, 0); @@ -2376,9 +2384,9 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, Item *item_having_part2= 0; for (uint i= 0; i < cols_num; i++) { - DBUG_ASSERT((left_expr->fixed && + DBUG_ASSERT((left_expr->is_fixed() && - select_lex->ref_pointer_array[i]->fixed) || + select_lex->ref_pointer_array[i]->is_fixed()) || (select_lex->ref_pointer_array[i]->type() == REF_ITEM && ((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() == Item_ref::OUTER_REF)); @@ -2447,8 +2455,8 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, for (uint i= 0; i < cols_num; i++) { Item *item, *item_isnull; - DBUG_ASSERT((left_expr->fixed && - select_lex->ref_pointer_array[i]->fixed) || + DBUG_ASSERT((left_expr->is_fixed() && + select_lex->ref_pointer_array[i]->is_fixed()) || (select_lex->ref_pointer_array[i]->type() == REF_ITEM && ((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() == Item_ref::OUTER_REF)); @@ -5802,14 +5810,14 @@ Ordered_key::cmp_keys_by_row_data(ha_rows a, ha_rows b) if (unlikely((error= tbl->file->ha_rnd_pos(tbl->record[0], rowid_a)))) { /* purecov: begin inspected */ - tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error + tbl->file->print_error(error, MYF(ME_FATAL)); // Sets fatal_error return 0; /* purecov: end */ } if (unlikely((error= tbl->file->ha_rnd_pos(tbl->record[1], rowid_b)))) { /* purecov: begin inspected */ - tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error + tbl->file->print_error(error, MYF(ME_FATAL)); // Sets fatal_error return 0; /* purecov: end */ } @@ -5891,7 +5899,7 @@ int Ordered_key::cmp_key_with_search_key(rownum_t row_num) if (unlikely((error= tbl->file->ha_rnd_pos(tbl->record[0], cur_rowid)))) { /* purecov: begin inspected */ - tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error + tbl->file->print_error(error, MYF(ME_FATAL)); // Sets fatal_error return 0; /* purecov: end */ } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 363dbba4ddd..443354f4900 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -33,6 +33,7 @@ class subselect_hash_sj_engine; class Item_bool_func2; class Comp_creator; class With_element; +class Field_pair; typedef class st_select_lex SELECT_LEX; @@ -46,7 +47,8 @@ class Cached_item; /* base class for subselects */ class Item_subselect :public Item_result_field, - protected Used_tables_and_const_cache + protected Used_tables_and_const_cache, + protected With_sum_func_cache { bool value_assigned; /* value already assigned to subselect */ bool own_engine; /* the engine was not taken from other Item_subselect */ @@ -183,6 +185,8 @@ public: } bool fix_fields(THD *thd, Item **ref); bool with_subquery() const { DBUG_ASSERT(fixed); return true; } + bool with_sum_func() const { return m_with_sum_func; } + With_sum_func_cache* get_with_sum_func_cache() { return this; } bool mark_as_dependent(THD *thd, st_select_lex *select, Item *item); void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); void recalc_used_tables(st_select_lex *new_parent, bool after_pullout); @@ -304,7 +308,7 @@ public: String *val_str (String *); my_decimal *val_decimal(my_decimal *); bool val_bool(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); const Type_handler *type_handler() const; bool fix_length_and_dec(); @@ -395,14 +399,14 @@ public: } void no_rows_in_result(); - const Type_handler *type_handler() const { return &type_handler_longlong; } + const Type_handler *type_handler() const { return &type_handler_bool; } longlong val_int(); double val_real(); String *val_str(String*); my_decimal *val_decimal(my_decimal *); bool val_bool(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_int(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_int(thd, ltime, fuzzydate); } bool fix_fields(THD *thd, Item **ref); bool fix_length_and_dec(); void print(String *str, enum_query_type query_type); @@ -570,6 +574,8 @@ public: */ bool is_registered_semijoin; + List<Field_pair> corresponding_fields; + /* Used to determine how this subselect item is represented in the item tree, in case there is a need to locate it there and replace with something else. @@ -621,7 +627,6 @@ public: double val_real(); String *val_str(String*); my_decimal *val_decimal(my_decimal *); - void update_null_value () { (void) val_bool(); } bool val_bool(); bool test_limit(st_select_lex_unit *unit); void print(String *str, enum_query_type query_type); @@ -741,6 +746,8 @@ public: return 0; }; + bool pushdown_cond_for_in_subquery(THD *thd, Item *cond); + friend class Item_ref_null_helper; friend class Item_is_not_null_test; friend class Item_in_optimizer; @@ -852,7 +859,6 @@ protected: bool set_row(List<Item> &item_list, Item_cache **row); }; - class subselect_single_select_engine: public subselect_engine { bool prepared; /* simple subselect is prepared */ @@ -886,9 +892,10 @@ public: friend class subselect_hash_sj_engine; friend class Item_in_subselect; - friend bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, - Item **join_where); - + friend bool execute_degenerate_jtbm_semi_join(THD *thd, + TABLE_LIST *tbl, + Item_in_subselect *subq_pred, + List<Item> &eq_list); }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 3163fb9ea2e..d1865c7e771 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -404,7 +404,7 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref) for (sl= thd->lex->current_select; sl && sl != aggr_sel && sl->master_unit()->item; sl= sl->master_unit()->outer_select() ) - sl->master_unit()->item->with_sum_func= 1; + sl->master_unit()->item->get_with_sum_func_cache()->set_with_sum_func(); } thd->lex->current_select->mark_as_dependent(thd, aggr_sel, NULL); @@ -484,7 +484,6 @@ void Item_sum::mark_as_sum_func() cur_select->n_sum_items++; cur_select->with_sum_func= 1; const_item_cache= false; - with_sum_func= 1; with_field= 0; window_func_sum_expr_flag= false; } @@ -890,7 +889,7 @@ bool Aggregator_distinct::setup(THD *thd) item_sum->null_value= item_sum->maybe_null= 1; item_sum->quick_group= 0; - DBUG_ASSERT(item_sum->get_arg(0)->fixed); + DBUG_ASSERT(item_sum->get_arg(0)->is_fixed()); arg= item_sum->get_arg(0); if (arg->const_item()) @@ -1237,9 +1236,11 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table) if (args[0]->type() == Item::FIELD_ITEM) { Field *field= ((Item_field*) args[0])->field; - if ((field= create_tmp_field_from_field(table->in_use, field, &name, - table, NULL))) - field->flags&= ~NOT_NULL_FLAG; + if ((field= field->create_tmp_field(table->in_use->mem_root, table, true))) + { + DBUG_ASSERT((field->flags & NOT_NULL_FLAG) == 0); + field->field_name= name; + } DBUG_RETURN(field); } DBUG_RETURN(tmp_table_field_from_field_type(table)); @@ -1287,7 +1288,7 @@ Item_sum_sp::fix_fields(THD *thd, Item **ref) if (!m_sp) { my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr()); - context->process_error(thd); + process_error(thd); return TRUE; } @@ -1639,12 +1640,7 @@ longlong Item_sum_sum::val_int() if (aggr) aggr->endup(); if (result_type() == DECIMAL_RESULT) - { - longlong result; - my_decimal2int(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, unsigned_flag, - &result); - return result; - } + return dec_buffs[curr_dec_buff].to_longlong(unsigned_flag); return val_int_from_real(); } @@ -1655,7 +1651,7 @@ double Item_sum_sum::val_real() if (aggr) aggr->endup(); if (result_type() == DECIMAL_RESULT) - my_decimal2double(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, &sum); + sum= dec_buffs[curr_dec_buff].to_double(); return sum; } @@ -1665,7 +1661,7 @@ String *Item_sum_sum::val_str(String *str) if (aggr) aggr->endup(); if (result_type() == DECIMAL_RESULT) - return val_string_from_decimal(str); + return VDec(this).to_string_round(str, decimals); return val_string_from_real(str); } @@ -2031,7 +2027,7 @@ String *Item_sum_avg::val_str(String *str) if (aggr) aggr->endup(); if (result_type() == DECIMAL_RESULT) - return val_string_from_decimal(str); + return VDec(this).to_string_round(str, decimals); return val_string_from_real(str); } @@ -2309,12 +2305,12 @@ void Item_sum_hybrid::clear() bool -Item_sum_hybrid::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +Item_sum_hybrid::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); if (null_value) return true; - bool retval= value->get_date(ltime, fuzzydate); + bool retval= value->get_date(thd, ltime, fuzzydate); if ((null_value= value->null_value)) DBUG_ASSERT(retval == true); return retval; @@ -2748,11 +2744,11 @@ void Item_sum_hybrid::reset_field() } case DECIMAL_RESULT: { - my_decimal value_buff, *arg_dec= arg0->val_decimal(&value_buff); + VDec arg_dec(arg0); if (maybe_null) { - if (arg0->null_value) + if (arg_dec.is_null()) result_field->set_null(); else result_field->set_notnull(); @@ -2761,9 +2757,7 @@ void Item_sum_hybrid::reset_field() We must store zero in the field as we will use the field value in add() */ - if (!arg_dec) // Null - arg_dec= &decimal_zero; - result_field->store_decimal(arg_dec); + result_field->store_decimal(arg_dec.ptr_or(&decimal_zero)); break; } case ROW_RESULT: @@ -2786,15 +2780,10 @@ void Item_sum_sum::reset_field() DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); if (result_type() == DECIMAL_RESULT) { - my_decimal value, *arg_val; if (unlikely(direct_added)) - arg_val= &direct_sum_decimal; + result_field->store_decimal(&direct_sum_decimal); else - { - if (!(arg_val= args[0]->val_decimal(&value))) - arg_val= &decimal_zero; // Null - } - result_field->store_decimal(arg_val); + result_field->store_decimal(VDec(args[0]).ptr_or(&decimal_zero)); } else { @@ -2847,15 +2836,9 @@ void Item_sum_avg::reset_field() if (result_type() == DECIMAL_RESULT) { longlong tmp; - my_decimal value, *arg_dec= args[0]->val_decimal(&value); - if (args[0]->null_value) - { - arg_dec= &decimal_zero; - tmp= 0; - } - else - tmp= 1; - my_decimal2binary(E_DEC_FATAL_ERROR, arg_dec, res, f_precision, f_scale); + VDec value(args[0]); + tmp= value.is_null() ? 0 : 1; + value.to_binary(res, f_precision, f_scale); res+= dec_bin_size; int8store(res, tmp); } @@ -2922,9 +2905,8 @@ void Item_sum_sum::update_field() { if (!result_field->is_null()) { - my_decimal field_value; - my_decimal *field_val= result_field->val_decimal(&field_value); - my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, field_val); + my_decimal field_value(result_field); + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, &field_value); result_field->store_decimal(dec_buffs); } else @@ -2991,15 +2973,14 @@ void Item_sum_avg::update_field() if (result_type() == DECIMAL_RESULT) { - my_decimal value, *arg_val= args[0]->val_decimal(&value); - if (!args[0]->null_value) + VDec tmp(args[0]); + if (!tmp.is_null()) { binary2my_decimal(E_DEC_FATAL_ERROR, res, dec_buffs + 1, f_precision, f_scale); field_count= sint8korr(res + dec_bin_size); - my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, dec_buffs + 1); - my_decimal2binary(E_DEC_FATAL_ERROR, dec_buffs, - res, f_precision, f_scale); + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, tmp.ptr(), dec_buffs + 1); + dec_buffs->to_binary(res, f_precision, f_scale); res+= dec_bin_size; field_count++; int8store(res, field_count); @@ -3194,9 +3175,7 @@ my_decimal *Item_avg_field_decimal::val_decimal(my_decimal *dec_buf) if ((null_value= !count)) return 0; - my_decimal dec_count, dec_field; - binary2my_decimal(E_DEC_FATAL_ERROR, - field->ptr, &dec_field, f_precision, f_scale); + my_decimal dec_count, dec_field(field->ptr, f_precision, f_scale); int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &dec_count); my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &dec_field, &dec_count, prec_increment); @@ -3310,24 +3289,6 @@ my_decimal *Item_sum_udf_float::val_decimal(my_decimal *dec) } -String *Item_sum_udf_decimal::val_str(String *str) -{ - return val_string_from_decimal(str); -} - - -double Item_sum_udf_decimal::val_real() -{ - return val_real_from_decimal(); -} - - -longlong Item_sum_udf_decimal::val_int() -{ - return val_int_from_decimal(); -} - - my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf) { my_decimal *res; @@ -4008,6 +3969,7 @@ bool Item_func_group_concat::setup(THD *thd) if (!ref_pointer_array) DBUG_RETURN(TRUE); memcpy(ref_pointer_array, args, arg_count * sizeof(Item*)); + DBUG_ASSERT(context); if (setup_order(thd, Ref_ptr_array(ref_pointer_array, n_elems), context->table_list, list, all_fields, *order)) DBUG_RETURN(TRUE); diff --git a/sql/item_sum.h b/sql/item_sum.h index b400ebd5f80..c88a850c241 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -511,7 +511,12 @@ public: } virtual void make_unique() { force_copy_fields= TRUE; } Item *get_tmp_table_item(THD *thd); - Field *create_tmp_field(bool group, TABLE *table); + virtual Field *create_tmp_field(bool group, TABLE *table); + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + return create_tmp_field(param->group(), table); + } virtual bool collect_outer_ref_processor(void *param); bool init_sum_func_check(THD *thd); bool check_sum_func(THD *thd, Item **ref); @@ -578,6 +583,8 @@ public: void mark_as_window_func_sum_expr() { window_func_sum_expr_flag= true; } bool is_window_func_sum_expr() { return window_func_sum_expr_flag; } virtual void setup_caches(THD *thd) {}; + + bool with_sum_func() const { return true; } }; @@ -735,9 +742,9 @@ public: longlong val_int() { return val_int_from_real(); /* Real as default */ } String *val_str(String*str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } void reset_field(); }; @@ -1060,7 +1067,7 @@ protected: double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); void reset_field(); String *val_str(String *); const Type_handler *real_type_handler() const @@ -1356,7 +1363,7 @@ public: void update_field(){DBUG_ASSERT(0);} void clear(); void cleanup(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { return execute() || sp_result_field->get_date(ltime, fuzzydate); } @@ -1384,17 +1391,21 @@ public: decimals= item->decimals; max_length= item->max_length; unsigned_flag= item->unsigned_flag; - fixed= true; } table_map used_tables() const { return (table_map) 1L; } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + return create_tmp_field_ex_simple(table, src, param); + } void save_in_result_field(bool no_conversions) { DBUG_ASSERT(0); } bool check_vcol_func_processor(void *arg) { return mark_unsupported_function(name.str, arg, VCOL_IMPOSSIBLE); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } }; @@ -1439,9 +1450,18 @@ public: dec_bin_size(item->dec_bin_size) { } const Type_handler *type_handler() const { return &type_handler_newdecimal; } - double val_real() { return val_real_from_decimal(); } - longlong val_int() { return val_int_from_decimal(); } - String *val_str(String *str) { return val_string_from_decimal(str); } + double val_real() + { + return VDec(this).to_double(); + } + longlong val_int() + { + return VDec(this).to_longlong(unsigned_flag); + } + String *val_str(String *str) + { + return VDec(this).to_string_round(str, decimals); + } my_decimal *val_decimal(my_decimal *); Item *get_copy(THD *thd) { return get_item_copy<Item_avg_field_decimal>(thd, this); } @@ -1545,9 +1565,9 @@ public: void update_field() {}; void cleanup(); virtual void print(String *str, enum_query_type query_type); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } }; @@ -1645,9 +1665,18 @@ public: Item_udf_sum(thd, udf_arg, list) {} Item_sum_udf_decimal(THD *thd, Item_sum_udf_decimal *item) :Item_udf_sum(thd, item) {} - String *val_str(String *); - double val_real(); - longlong val_int(); + String *val_str(String *str) + { + return VDec(this).to_string_round(str, decimals); + } + double val_real() + { + return VDec(this).to_double(); + } + longlong val_int() + { + return VDec(this).to_longlong(unsigned_flag); + } my_decimal *val_decimal(my_decimal *); const Type_handler *type_handler() const { return &type_handler_newdecimal; } bool fix_length_and_dec() { fix_num_length_and_dec(); return FALSE; } @@ -1845,9 +1874,9 @@ public: { return val_decimal_from_string(decimal_value); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return get_date_from_string(ltime, fuzzydate); + return get_date_from_string(thd, ltime, fuzzydate); } String* val_str(String* str); Item *copy_or_same(THD* thd); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 4d17bc354d4..f12c8e12668 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -59,6 +59,29 @@ /** Day number for Dec 31st, 9999. */ #define MAX_DAY_NUMBER 3652424L + +Func_handler_date_add_interval_datetime_arg0_time + func_handler_date_add_interval_datetime_arg0_time; + +Func_handler_date_add_interval_datetime func_handler_date_add_interval_datetime; +Func_handler_date_add_interval_date func_handler_date_add_interval_date; +Func_handler_date_add_interval_time func_handler_date_add_interval_time; +Func_handler_date_add_interval_string func_handler_date_add_interval_string; + +Func_handler_add_time_datetime func_handler_add_time_datetime_add(1); +Func_handler_add_time_datetime func_handler_add_time_datetime_sub(-1); +Func_handler_add_time_time func_handler_add_time_time_add(1); +Func_handler_add_time_time func_handler_add_time_time_sub(-1); +Func_handler_add_time_string func_handler_add_time_string_add(1); +Func_handler_add_time_string func_handler_add_time_string_sub(-1); + +Func_handler_str_to_date_datetime_sec func_handler_str_to_date_datetime_sec; +Func_handler_str_to_date_datetime_usec func_handler_str_to_date_datetime_usec; +Func_handler_str_to_date_date func_handler_str_to_date_date; +Func_handler_str_to_date_time_sec func_handler_str_to_date_time_sec; +Func_handler_str_to_date_time_usec func_handler_str_to_date_time_usec; + + /* Date formats corresponding to compound %r and %T conversion specifiers @@ -103,12 +126,12 @@ static DATE_TIME_FORMAT time_24hrs_format= {{0}, '\0', 0, 1 error */ -static bool extract_date_time(DATE_TIME_FORMAT *format, +static bool extract_date_time(THD *thd, DATE_TIME_FORMAT *format, const char *val, uint length, MYSQL_TIME *l_time, timestamp_type cached_timestamp_type, const char **sub_pattern_end, const char *date_time_type, - ulonglong fuzzy_date) + date_mode_t fuzzydate) { int weekday= 0, yearday= 0, daypart= 0; int week_number= -1; @@ -303,17 +326,17 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, We can't just set error here, as we don't want to generate two warnings in case of errors */ - if (extract_date_time(&time_ampm_format, val, + if (extract_date_time(thd, &time_ampm_format, val, (uint)(val_end - val), l_time, - cached_timestamp_type, &val, "time", fuzzy_date)) + cached_timestamp_type, &val, "time", fuzzydate)) DBUG_RETURN(1); break; /* Time in 24-hour notation */ case 'T': - if (extract_date_time(&time_24hrs_format, val, + if (extract_date_time(thd, &time_24hrs_format, val, (uint)(val_end - val), l_time, - cached_timestamp_type, &val, "time", fuzzy_date)) + cached_timestamp_type, &val, "time", fuzzydate)) DBUG_RETURN(1); break; @@ -419,7 +442,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, goto err; int was_cut; - if (check_date(l_time, fuzzy_date | TIME_INVALID_DATES, &was_cut)) + if (check_date(l_time, fuzzydate | TIME_INVALID_DATES, &was_cut)) goto err; if (val != val_end) @@ -428,10 +451,9 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, { if (!my_isspace(&my_charset_latin1,*val)) { - make_truncated_value_warning(current_thd, - Sql_condition::WARN_LEVEL_WARN, - val_begin, length, - cached_timestamp_type, NullS); + ErrConvString err(val_begin, length, &my_charset_bin); + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, + &err, cached_timestamp_type, NullS); break; } } while (++val != val_end); @@ -440,7 +462,6 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, err: { - THD *thd= current_thd; char buff[128]; strmake(buff, val_begin, MY_MIN(length, sizeof(buff)-1)); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -787,10 +808,8 @@ longlong Item_func_period_diff::val_int() longlong Item_func_to_days::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return 0; - return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day); + Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + return (null_value= !d.is_valid_date()) ? 0 : d.daynr(); } @@ -798,42 +817,26 @@ longlong Item_func_to_seconds::val_int_endpoint(bool left_endp, bool *incl_endp) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - longlong seconds; - longlong days; - int dummy; /* unused */ - if (get_arg0_date(<ime, TIME_FUZZY_DATES)) + Datetime dt(current_thd, args[0], TIME_FUZZY_DATES); + if ((null_value= !dt.is_valid_datetime())) { /* got NULL, leave the incl_endp intact */ return LONGLONG_MIN; } - seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second; - seconds= ltime.neg ? -seconds : seconds; - days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day); - seconds+= days * 24L * 3600L; /* Set to NULL if invalid date, but keep the value */ - null_value= check_date(<ime, - (ltime.year || ltime.month || ltime.day), - (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE), - &dummy); + null_value= dt.check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE); /* Even if the evaluation return NULL, seconds is useful for pruning */ - return seconds; + return dt.to_seconds(); } longlong Item_func_to_seconds::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - longlong seconds; - longlong days; - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return 0; - seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second; - seconds=ltime.neg ? -seconds : seconds; - days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day); - return seconds + days * 24L * 3600L; + THD *thd= current_thd; + Datetime dt(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + return (null_value= !dt.is_valid_datetime()) ? 0 : dt.to_seconds(); } /* @@ -877,19 +880,16 @@ enum_monotonicity_info Item_func_to_seconds::get_monotonicity_info() const longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; + Datetime dt(current_thd, args[0], date_mode_t(0)); longlong res; - int dummy; /* unused */ - if (get_arg0_date(<ime, 0)) + if ((null_value= !dt.is_valid_datetime())) { /* got NULL, leave the incl_endp intact */ return LONGLONG_MIN; } - res=(longlong) calc_daynr(ltime.year,ltime.month,ltime.day); + res= (longlong) dt.daynr(); /* Set to NULL if invalid date, but keep the value */ - null_value= check_date(<ime, - (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE), - &dummy); + null_value= dt.check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE); if (null_value) { /* @@ -918,8 +918,8 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) col < '2007-09-15 12:34:56' -> TO_DAYS(col) <= TO_DAYS('2007-09-15') */ - if ((!left_endp && !(ltime.hour || ltime.minute || ltime.second || - ltime.second_part)) || + const MYSQL_TIME <ime= dt.get_mysql_time()[0]; + if ((!left_endp && dt.hhmmssff_is_zero()) || (left_endp && ltime.hour == 23 && ltime.minute == 59 && ltime.second == 59)) /* do nothing */ @@ -933,25 +933,22 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) longlong Item_func_dayofyear::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE)) - return 0; - return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day) - - calc_daynr(ltime.year,1,1) + 1; + Date d(current_thd, args[0], TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE); + return (null_value= !d.is_valid_date()) ? 0 : d.dayofyear(); } longlong Item_func_dayofmonth::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - return get_arg0_date(<ime, 0) ? 0 : (longlong) ltime.day; + Date d(current_thd, args[0], date_mode_t(0)); + return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->day; } longlong Item_func_month::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - return get_arg0_date(<ime, 0) ? 0 : (longlong) ltime.month; + Date d(current_thd, args[0], date_mode_t(0)); + return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->month; } @@ -973,12 +970,12 @@ String* Item_func_monthname::val_str(String* str) DBUG_ASSERT(fixed == 1); const char *month_name; uint err; - MYSQL_TIME ltime; + Date d(current_thd, args[0], date_mode_t(0)); - if ((null_value= (get_arg0_date(<ime, 0) || !ltime.month))) + if ((null_value= (!d.is_valid_date() || !d.get_mysql_time()->month))) return (String *) 0; - month_name= locale->month_names->type_names[ltime.month - 1]; + month_name= locale->month_names->type_names[d.get_mysql_time()->month - 1]; str->copy(month_name, (uint) strlen(month_name), &my_charset_utf8_bin, collation.collation, &err); return str; @@ -992,23 +989,21 @@ String* Item_func_monthname::val_str(String* str) longlong Item_func_quarter::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_arg0_date(<ime, 0)) - return 0; - return (longlong) ((ltime.month+2)/3); + Date d(current_thd, args[0], date_mode_t(0)); + return (null_value= !d.is_valid_date()) ? 0 : d.quarter(); } longlong Item_func_hour::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->hour; } longlong Item_func_minute::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->minute; } @@ -1018,7 +1013,7 @@ longlong Item_func_minute::val_int() longlong Item_func_second::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->second; } @@ -1065,43 +1060,34 @@ uint week_mode(uint mode) longlong Item_func_week::val_int() { DBUG_ASSERT(fixed == 1); - uint year, week_format; - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) + uint week_format; + THD *thd= current_thd; + Date d(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + if ((null_value= !d.is_valid_date())) return 0; if (arg_count > 1) week_format= (uint)args[1]->val_int(); else - week_format= current_thd->variables.default_week_format; - return (longlong) calc_week(<ime, week_mode(week_format), &year); + week_format= thd->variables.default_week_format; + return d.week(week_mode(week_format)); } longlong Item_func_yearweek::val_int() { DBUG_ASSERT(fixed == 1); - uint year,week; - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return 0; - week= calc_week(<ime, - (week_mode((uint) args[1]->val_int()) | WEEK_YEAR), - &year); - return week+year*100; + Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + return (null_value= !d.is_valid_date()) ? 0 : + d.yearweek((week_mode((uint) args[1]->val_int()) | WEEK_YEAR)); } longlong Item_func_weekday::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return 0; - - return (longlong) calc_weekday(calc_daynr(ltime.year, ltime.month, - ltime.day), - odbc_type) + MY_TEST(odbc_type); + Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + return ((null_value= !d.is_valid_date())) ? 0 : + calc_weekday(d.daynr(), odbc_type) + MY_TEST(odbc_type); } bool Item_func_dayname::fix_length_and_dec() @@ -1137,8 +1123,8 @@ String* Item_func_dayname::val_str(String* str) longlong Item_func_year::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - return get_arg0_date(<ime, 0) ? 0 : (longlong) ltime.year; + Date d(current_thd, args[0], date_mode_t(0)); + return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->year; } @@ -1169,8 +1155,8 @@ enum_monotonicity_info Item_func_year::get_monotonicity_info() const longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_arg0_date(<ime, 0)) + Datetime dt(current_thd, args[0], date_mode_t(0)); + if ((null_value= !dt.is_valid_datetime())) { /* got NULL, leave the incl_endp intact */ return LONGLONG_MIN; @@ -1187,8 +1173,9 @@ longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp) col < '2007-09-15 23:00:00' -> YEAR(col) <= 2007 */ + const MYSQL_TIME <ime= dt.get_mysql_time()[0]; if (!left_endp && ltime.day == 1 && ltime.month == 1 && - !(ltime.hour || ltime.minute || ltime.second || ltime.second_part)) + dt.hhmmssff_is_zero()) ; /* do nothing */ else *incl_endp= TRUE; @@ -1212,13 +1199,14 @@ bool Item_func_unix_timestamp::get_timestamp_value(my_time_t *seconds, } } - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_IN_DATE)) - return 1; + THD *thd= current_thd; + Datetime dt(thd, args[0], TIME_NO_ZERO_IN_DATE); + if ((null_value= !dt.is_valid_datetime())) + return true; uint error_code; - *seconds= TIME_to_timestamp(current_thd, <ime, &error_code); - *second_part= ltime.second_part; + *seconds= TIME_to_timestamp(thd, dt.get_mysql_time(), &error_code); + *second_part= dt.get_mysql_time()->second_part; return (null_value= (error_code == ER_WARN_DATA_OUT_OF_RANGE)); } @@ -1276,7 +1264,7 @@ longlong Item_func_unix_timestamp::val_int_endpoint(bool left_endp, bool *incl_e longlong Item_func_time_to_sec::int_op() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return ((null_value= !tm.is_valid_time())) ? 0 : tm.to_seconds(); } @@ -1284,7 +1272,7 @@ longlong Item_func_time_to_sec::int_op() my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf) { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); if ((null_value= !tm.is_valid_time())) return 0; const MYSQL_TIME *ltime= tm.get_mysql_time(); @@ -1299,7 +1287,8 @@ my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf) To make code easy, allow interval objects without separators. */ -bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval) +bool get_interval_value(THD *thd, Item *args, + interval_type int_type, INTERVAL *interval) { ulonglong array[5]; longlong UNINIT_VAR(value); @@ -1312,25 +1301,19 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval) bzero((char*) interval,sizeof(*interval)); if (int_type == INTERVAL_SECOND && args->decimals) { - my_decimal decimal_value, *val; - ulonglong second; - ulong second_part; - if (!(val= args->val_decimal(&decimal_value))) + VDec val(args); + if (val.is_null()) return true; - interval->neg= my_decimal2seconds(val, &second, &second_part); - if (second == LONGLONG_MAX) + Sec6 d(val.ptr()); + interval->neg= d.neg(); + if (d.sec() >= LONGLONG_MAX) { - THD *thd= current_thd; - ErrConvDecimal err(val); - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), "DECIMAL", - err.ptr()); + ErrConvDecimal err(val.ptr()); + thd->push_warning_truncated_wrong_value("seconds", err.ptr()); return true; } - - interval->second= second; - interval->second_part= second_part; + interval->second= d.sec(); + interval->second_part= d.usec(); return false; } else if ((int) int_type <= INTERVAL_MICROSECOND) @@ -1476,90 +1459,11 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval) } -String *Item_temporal_func::val_str(String *str) -{ - DBUG_ASSERT(fixed == 1); - return val_string_from_date(str); -} - - -bool Item_temporal_hybrid_func::fix_temporal_type(MYSQL_TIME *ltime) -{ - if (ltime->time_type < 0) /* MYSQL_TIMESTAMP_NONE, MYSQL_TIMESTAMP_ERROR */ - return false; - - if (ltime->time_type != MYSQL_TIMESTAMP_TIME) - goto date_or_datetime_value; - - /* Convert TIME to DATE or DATETIME */ - switch (field_type()) - { - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - { - MYSQL_TIME tmp; - if (time_to_datetime_with_warn(current_thd, ltime, &tmp, 0)) - return (null_value= true); - *ltime= tmp; - if (field_type() == MYSQL_TYPE_DATE) - datetime_to_date(ltime); - return false; - } - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */ - return false; - default: - DBUG_ASSERT(0); - return (null_value= true); - } - -date_or_datetime_value: - /* Convert DATE or DATETIME to TIME, DATE, or DATETIME */ - switch (field_type()) - { - case MYSQL_TYPE_TIME: - datetime_to_time(ltime); - return false; - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - date_to_datetime(ltime); - return false; - case MYSQL_TYPE_DATE: - datetime_to_date(ltime); - return false; - case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */ - return false; - default: - DBUG_ASSERT(0); - return (null_value= true); - } - return false; -} - - -String *Item_temporal_hybrid_func::val_str_ascii(String *str) -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - - if (get_date(<ime, 0) || fix_temporal_type(<ime) || - (null_value= my_TIME_to_str(<ime, str, decimals))) - return (String *) 0; - - /* Check that the returned timestamp type matches to the function type */ - DBUG_ASSERT(field_type() == MYSQL_TYPE_STRING || - ltime.time_type == MYSQL_TIMESTAMP_NONE || - ltime.time_type == mysql_timestamp_type()); - return str; -} - - -bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_from_days::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { longlong value=args[0]->val_int(); if ((null_value= (args[0]->null_value || - ((fuzzy_date & TIME_NO_ZERO_DATE) && value == 0)))) + ((fuzzydate & TIME_NO_ZERO_DATE) && value == 0)))) return true; bzero(ltime, sizeof(MYSQL_TIME)); if (get_date_from_daynr((long) value, <ime->year, <ime->month, @@ -1596,10 +1500,9 @@ void Item_func_curdate_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) } -bool Item_func_curdate::get_date(MYSQL_TIME *res, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_curdate::get_date(THD *thd, MYSQL_TIME *res, + date_mode_t fuzzydate __attribute__((unused))) { - THD *thd= current_thd; query_id_t query_id= thd->query_id; /* Cache value for this query */ if (last_query_id != query_id) @@ -1626,10 +1529,9 @@ bool Item_func_curtime::fix_fields(THD *thd, Item **items) return Item_timefunc::fix_fields(thd, items); } -bool Item_func_curtime::get_date(MYSQL_TIME *res, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_curtime::get_date(THD *thd, MYSQL_TIME *res, + date_mode_t fuzzydate __attribute__((unused))) { - THD *thd= current_thd; query_id_t query_id= thd->query_id; /* Cache value for this query */ if (last_query_id != query_id) @@ -1700,7 +1602,7 @@ bool Item_func_now::fix_fields(THD *thd, Item **items) func_name(), TIME_SECOND_PART_DIGITS); return 1; } - return Item_temporal_func::fix_fields(thd, items); + return Item_datetimefunc::fix_fields(thd, items); } void Item_func_now::print(String *str, enum_query_type query_type) @@ -1727,7 +1629,7 @@ int Item_func_now_local::save_in_field(Field *field, bool no_conversions) return 0; } else - return Item_temporal_func::save_in_field(field, no_conversions); + return Item_datetimefunc::save_in_field(field, no_conversions); } @@ -1758,10 +1660,9 @@ void Item_func_now_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) } -bool Item_func_now::get_date(MYSQL_TIME *res, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_now::get_date(THD *thd, MYSQL_TIME *res, + date_mode_t fuzzydate __attribute__((unused))) { - THD *thd= current_thd; query_id_t query_id= thd->query_id; /* Cache value for this query */ if (last_query_id != query_id) @@ -1787,62 +1688,22 @@ void Item_func_sysdate_local::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) } -bool Item_func_sysdate_local::get_date(MYSQL_TIME *res, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_sysdate_local::get_date(THD *thd, MYSQL_TIME *res, + date_mode_t fuzzydate __attribute__((unused))) { - store_now_in_TIME(current_thd, res); + store_now_in_TIME(thd, res); return 0; } -bool Item_func_sec_to_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_sec_to_time::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); - bool sign; - ulonglong sec; - ulong sec_part; - - bzero((char *)ltime, sizeof(*ltime)); - ltime->time_type= MYSQL_TIMESTAMP_TIME; - - sign= args[0]->get_seconds(&sec, &sec_part); - - if ((null_value= args[0]->null_value)) - return 1; - - ltime->neg= sign; - if (sec > TIME_MAX_VALUE_SECONDS) - goto overflow; - - DBUG_ASSERT(sec_part <= TIME_MAX_SECOND_PART); - - ltime->hour= (uint) (sec/3600); - ltime->minute= (uint) (sec % 3600) /60; - ltime->second= (uint) sec % 60; - ltime->second_part= sec_part; - - return 0; - -overflow: - /* use check_time_range() to set ltime to the max value depending on dec */ - int unused; - char buf[100]; - String tmp(buf, sizeof(buf), &my_charset_bin), *err= args[0]->val_str(&tmp); - - ltime->hour= TIME_MAX_HOUR+1; - check_time_range(ltime, decimals, &unused); - if (!err) - { - ErrConvInteger err2(sec, unsigned_flag); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - &err2, MYSQL_TIMESTAMP_TIME, NullS); - } - else - { - ErrConvString err2(err); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - &err2, MYSQL_TIMESTAMP_TIME, NullS); - } - return 0; + VSec6 sec(thd, args[0], "seconds", LONGLONG_MAX); + if ((null_value= sec.is_null())) + return true; + if (sec.sec_to_time(ltime, decimals) && !sec.truncated()) + sec.make_truncated_warning(thd, "seconds"); + return false; } bool Item_func_date_format::fix_length_and_dec() @@ -1998,7 +1859,9 @@ String *Item_func_date_format::val_str(String *str) const MY_LOCALE *lc= 0; DBUG_ASSERT(fixed == 1); - if ((null_value= args[0]->get_date(&l_time, is_time_format ? TIME_TIME_ONLY : 0))) + if ((null_value= args[0]->get_date(current_thd, &l_time, + is_time_format ? TIME_TIME_ONLY : + date_mode_t(0)))) return 0; if (!(format = args[1]->val_str(str)) || !format->length()) @@ -2049,35 +1912,30 @@ bool Item_func_from_unixtime::fix_length_and_dec() } -bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_from_unixtime::get_date(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate __attribute__((unused))) { - bool sign; - ulonglong sec; - ulong sec_part; - bzero((char *)ltime, sizeof(*ltime)); ltime->time_type= MYSQL_TIMESTAMP_TIME; - sign= args[0]->get_seconds(&sec, &sec_part); + VSec6 sec(thd, args[0], "unixtime", TIMESTAMP_MAX_VALUE); + DBUG_ASSERT(sec.sec() <= TIMESTAMP_MAX_VALUE); - if (args[0]->null_value || sign || sec > TIMESTAMP_MAX_VALUE) + if (sec.is_null() || sec.truncated() || sec.neg()) return (null_value= 1); - tz->gmt_sec_to_TIME(ltime, (my_time_t)sec); - - ltime->second_part= sec_part; + tz->gmt_sec_to_TIME(ltime, (my_time_t) sec.sec()); + ltime->second_part= sec.usec(); return (null_value= 0); } -bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_convert_tz::get_date(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate __attribute__((unused))) { my_time_t my_time_tmp; String str; - THD *thd= current_thd; if (!from_tz_cached) { @@ -2091,9 +1949,12 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, to_tz_cached= args[2]->const_item(); } - if (from_tz==0 || to_tz==0 || - get_arg0_date(ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return (null_value= 1); + if ((null_value= (from_tz == 0 || to_tz == 0))) + return true; + + Datetime *dt= new(ltime) Datetime(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + if ((null_value= !dt->is_valid_datetime())) + return true; { uint not_used; @@ -2113,7 +1974,7 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, void Item_func_convert_tz::cleanup() { from_tz_cached= to_tz_cached= 0; - Item_temporal_func::cleanup(); + Item_datetimefunc::cleanup(); } @@ -2144,81 +2005,44 @@ bool Item_date_add_interval::fix_length_and_dec() MYSQL_TIME or DATETIME argument) */ arg0_field_type= args[0]->field_type(); - uint interval_dec= 0; - if (int_type == INTERVAL_MICROSECOND || - (int_type >= INTERVAL_DAY_MICROSECOND && - int_type <= INTERVAL_SECOND_MICROSECOND)) - interval_dec= TIME_SECOND_PART_DIGITS; - else if (int_type == INTERVAL_SECOND && args[1]->decimals > 0) - interval_dec= MY_MIN(args[1]->decimals, TIME_SECOND_PART_DIGITS); if (arg0_field_type == MYSQL_TYPE_DATETIME || arg0_field_type == MYSQL_TYPE_TIMESTAMP) { - uint dec= MY_MAX(args[0]->datetime_precision(), interval_dec); - set_handler(&type_handler_datetime); - fix_attributes_datetime(dec); + set_func_handler(&func_handler_date_add_interval_datetime); } else if (arg0_field_type == MYSQL_TYPE_DATE) { if (int_type <= INTERVAL_DAY || int_type == INTERVAL_YEAR_MONTH) - { - set_handler(&type_handler_newdate); - fix_attributes_date(); - } + set_func_handler(&func_handler_date_add_interval_date); else - { - set_handler(&type_handler_datetime2); - fix_attributes_datetime(interval_dec); - } + set_func_handler(&func_handler_date_add_interval_datetime); } else if (arg0_field_type == MYSQL_TYPE_TIME) { - uint dec= MY_MAX(args[0]->time_precision(), interval_dec); if (int_type >= INTERVAL_DAY && int_type != INTERVAL_YEAR_MONTH) - { - set_handler(&type_handler_time2); - fix_attributes_time(dec); - } + set_func_handler(&func_handler_date_add_interval_time); else - { - set_handler(&type_handler_datetime2); - fix_attributes_datetime(dec); - } + set_func_handler(&func_handler_date_add_interval_datetime_arg0_time); } else { - uint dec= MY_MAX(args[0]->datetime_precision(), interval_dec); - set_handler(&type_handler_string); - collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); - fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); + set_func_handler(&func_handler_date_add_interval_string); } maybe_null= true; - return FALSE; + return m_func_handler->fix_length_and_dec(this); } -bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Func_handler_date_add_interval_datetime_arg0_time:: + get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { - INTERVAL interval; - - if (args[0]->get_date(ltime, - field_type() == MYSQL_TYPE_TIME ? - TIME_TIME_ONLY : 0) || - get_interval_value(args[1], int_type, &interval)) - return (null_value=1); - - if (ltime->time_type != MYSQL_TIMESTAMP_TIME && - check_date_with_warn(ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE, - MYSQL_TIMESTAMP_ERROR)) - return (null_value=1); - - if (date_sub_interval) - interval.neg = !interval.neg; - - if (date_add_interval(ltime, int_type, interval)) - return (null_value=1); - return (null_value= 0); + // time_expr + INTERVAL {YEAR|QUARTER|MONTH|WEEK|YEAR_MONTH} + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_DATETIME_FUNCTION_OVERFLOW, + ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW), "time"); + return (item->null_value= true); } @@ -2274,7 +2098,7 @@ bool Item_extract::fix_length_and_dec() case INTERVAL_QUARTER: set_date_length(2); break; // 1..4 case INTERVAL_MONTH: set_date_length(2); break; // MM case INTERVAL_WEEK: set_date_length(2); break; // 0..52 - case INTERVAL_DAY: set_date_length(2); break; // DD + case INTERVAL_DAY: set_day_length(2); break; // DD case INTERVAL_DAY_HOUR: set_time_length(4); break; // DDhh case INTERVAL_DAY_MINUTE: set_time_length(6); break; // DDhhmm case INTERVAL_DAY_SECOND: set_time_length(8); break; // DDhhmmss @@ -2295,69 +2119,45 @@ bool Item_extract::fix_length_and_dec() } -longlong Item_extract::val_int() +uint Extract_source::week(THD *thd) const { - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; + DBUG_ASSERT(is_valid_extract_source()); uint year; - ulong week_format; - long neg; - int is_time_flag = date_value ? 0 : TIME_TIME_ONLY; - - // Not using get_arg0_date to avoid automatic TIME to DATETIME conversion - if ((null_value= args[0]->get_date(<ime, is_time_flag))) - return 0; - - neg= ltime.neg ? -1 : 1; + ulong week_format= current_thd->variables.default_week_format; + return calc_week(this, week_mode(week_format), &year); +} - DBUG_ASSERT(ltime.time_type != MYSQL_TIMESTAMP_TIME || ltime.day == 0); - if (ltime.time_type == MYSQL_TIMESTAMP_TIME) - time_to_daytime_interval(<ime); +longlong Item_extract::val_int() +{ + DBUG_ASSERT(fixed == 1); + Extract_source dt(current_thd, args[0], m_date_mode); + if ((null_value= !dt.is_valid_extract_source())) + return 0; switch (int_type) { - case INTERVAL_YEAR: return ltime.year; - case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month; - case INTERVAL_QUARTER: return (ltime.month+2)/3; - case INTERVAL_MONTH: return ltime.month; - case INTERVAL_WEEK: - { - week_format= current_thd->variables.default_week_format; - return calc_week(<ime, week_mode(week_format), &year); - } - case INTERVAL_DAY: return ltime.day; - case INTERVAL_DAY_HOUR: return (long) (ltime.day*100L+ltime.hour)*neg; - case INTERVAL_DAY_MINUTE: return (long) (ltime.day*10000L+ - ltime.hour*100L+ - ltime.minute)*neg; - case INTERVAL_DAY_SECOND: return ((longlong) ltime.day*1000000L+ - (longlong) (ltime.hour*10000L+ - ltime.minute*100+ - ltime.second))*neg; - case INTERVAL_HOUR: return (long) ltime.hour*neg; - case INTERVAL_HOUR_MINUTE: return (long) (ltime.hour*100+ltime.minute)*neg; - case INTERVAL_HOUR_SECOND: return (long) (ltime.hour*10000+ltime.minute*100+ - ltime.second)*neg; - case INTERVAL_MINUTE: return (long) ltime.minute*neg; - case INTERVAL_MINUTE_SECOND: return (long) (ltime.minute*100+ltime.second)*neg; - case INTERVAL_SECOND: return (long) ltime.second*neg; - case INTERVAL_MICROSECOND: return (long) ltime.second_part*neg; - case INTERVAL_DAY_MICROSECOND: return (((longlong)ltime.day*1000000L + - (longlong)ltime.hour*10000L + - ltime.minute*100 + - ltime.second)*1000000L + - ltime.second_part)*neg; - case INTERVAL_HOUR_MICROSECOND: return (((longlong)ltime.hour*10000L + - ltime.minute*100 + - ltime.second)*1000000L + - ltime.second_part)*neg; - case INTERVAL_MINUTE_MICROSECOND: return (((longlong)(ltime.minute*100+ - ltime.second))*1000000L+ - ltime.second_part)*neg; - case INTERVAL_SECOND_MICROSECOND: return ((longlong)ltime.second*1000000L+ - ltime.second_part)*neg; + case INTERVAL_YEAR: return dt.year(); + case INTERVAL_YEAR_MONTH: return dt.year_month(); + case INTERVAL_QUARTER: return dt.quarter(); + case INTERVAL_MONTH: return dt.month(); + case INTERVAL_WEEK: return dt.week(current_thd); + case INTERVAL_DAY: return dt.day(); + case INTERVAL_DAY_HOUR: return dt.day_hour(); + case INTERVAL_DAY_MINUTE: return dt.day_minute(); + case INTERVAL_DAY_SECOND: return dt.day_second(); + case INTERVAL_HOUR: return dt.hour(); + case INTERVAL_HOUR_MINUTE: return dt.hour_minute(); + case INTERVAL_HOUR_SECOND: return dt.hour_second(); + case INTERVAL_MINUTE: return dt.minute(); + case INTERVAL_MINUTE_SECOND: return dt.minute_second(); + case INTERVAL_SECOND: return dt.second(); + case INTERVAL_MICROSECOND: return dt.microsecond(); + case INTERVAL_DAY_MICROSECOND: return dt.day_microsecond(); + case INTERVAL_HOUR_MICROSECOND: return dt.hour_microsecond(); + case INTERVAL_MINUTE_MICROSECOND: return dt.minute_microsecond(); + case INTERVAL_SECOND_MICROSECOND: return dt.second_microsecond(); case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */ } - return 0; // Impossible + return 0; // Impossible } bool Item_extract::eq(const Item *item, bool binary_cmp) const @@ -2396,13 +2196,14 @@ bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const return 1; } -void Item_temporal_typecast::print(String *str, enum_query_type query_type) +void Item_func::print_cast_temporal(String *str, enum_query_type query_type) { char buf[32]; str->append(STRING_WITH_LEN("cast(")); args[0]->print(str, query_type); str->append(STRING_WITH_LEN(" as ")); - str->append(cast_type()); + const Name name= type_handler()->name(); + str->append(name.ptr(), name.length()); if (decimals && decimals != NOT_FIXED_DEC) { str->append('('); @@ -2615,45 +2416,28 @@ void Item_char_typecast::fix_length_and_dec_internal(CHARSET_INFO *from_cs) } -bool Item_time_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_time_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - Time tm(args[0], Time::Options_for_cast()); - if ((null_value= !tm.is_valid_time())) - return true; - tm.copy_to_mysql_time(ltime); - if (decimals < TIME_SECOND_PART_DIGITS) - my_time_trunc(ltime, decimals); - return (fuzzy_date & TIME_TIME_ONLY) ? 0 : - (null_value= check_date_with_warn(ltime, fuzzy_date, - MYSQL_TIMESTAMP_ERROR)); + Time *tm= new(ltime) Time(thd, args[0], Time::Options_for_cast(), + MY_MIN(decimals, TIME_SECOND_PART_DIGITS)); + return (null_value= !tm->is_valid_time()); } -bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_date_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - fuzzy_date |= sql_mode_for_dates(current_thd); - if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY)) - return 1; - - if (make_date_with_warn(ltime, fuzzy_date, MYSQL_TIMESTAMP_DATE)) - return (null_value= 1); - - return 0; + date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY; + Date *d= new(ltime) Date(thd, args[0], tmp); + return (null_value= !d->is_valid_date()); } -bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_datetime_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - fuzzy_date |= sql_mode_for_dates(current_thd); - if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY)) - return 1; - - if (decimals < TIME_SECOND_PART_DIGITS) - my_time_trunc(ltime, decimals); - - DBUG_ASSERT(ltime->time_type != MYSQL_TIMESTAMP_TIME); - ltime->time_type= MYSQL_TIMESTAMP_DATETIME; - return 0; + date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY; + Datetime *dt= new(ltime) Datetime(thd, args[0], tmp, + MY_MIN(decimals, TIME_SECOND_PART_DIGITS)); + return (null_value= !dt->is_valid_datetime()); } @@ -2668,20 +2452,17 @@ bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) 0099-12-31 */ -bool Item_func_makedate::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_makedate::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); - long daynr= (long) args[1]->val_int(); - long year= (long) args[0]->val_int(); - long days; + long year, days, daynr= (long) args[1]->val_int(); - if (args[0]->null_value || args[1]->null_value || - year < 0 || year > 9999 || daynr <= 0) + VYear vyear(args[0]); + if (vyear.is_null() || args[1]->null_value || vyear.truncated() || daynr <= 0) goto err; - if (year < 100) + if ((year= (long) vyear.year()) < 100) year= year_2000_handling(year); - days= calc_daynr(year,1,1) + daynr - 1; if (get_date_from_daynr(days, <ime->year, <ime->month, <ime->day)) goto err; @@ -2720,101 +2501,24 @@ bool Item_func_add_time::fix_length_and_dec() arg0_field_type= args[0]->field_type(); if (arg0_field_type == MYSQL_TYPE_DATE || arg0_field_type == MYSQL_TYPE_DATETIME || - arg0_field_type == MYSQL_TYPE_TIMESTAMP || - is_date) + arg0_field_type == MYSQL_TYPE_TIMESTAMP) { - uint dec= MY_MAX(args[0]->datetime_precision(), args[1]->time_precision()); - set_handler(&type_handler_datetime2); - fix_attributes_datetime(dec); + set_func_handler(sign > 0 ? &func_handler_add_time_datetime_add : + &func_handler_add_time_datetime_sub); } else if (arg0_field_type == MYSQL_TYPE_TIME) { - uint dec= MY_MAX(args[0]->time_precision(), args[1]->time_precision()); - set_handler(&type_handler_time2); - fix_attributes_time(dec); - } - else - { - uint dec= MY_MAX(args[0]->decimals, args[1]->decimals); - set_handler(&type_handler_string); - collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); - fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); - } - maybe_null= true; - return FALSE; -} - -/** - ADDTIME(t,a) and SUBTIME(t,a) are time functions that calculate a - time/datetime value - - t: time_or_datetime_expression - a: time_expression - - Result: Time value or datetime value -*/ - -bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME l_time1, l_time2; - bool is_time= 0; - long days, microseconds; - longlong seconds; - int l_sign= sign; - - if (Item_func_add_time::field_type() == MYSQL_TYPE_DATETIME) - { - // TIMESTAMP function OR the first argument is DATE/DATETIME/TIMESTAMP - if (get_arg0_date(&l_time1, 0) || - args[1]->get_time(&l_time2) || - l_time1.time_type == MYSQL_TIMESTAMP_TIME || - l_time2.time_type != MYSQL_TIMESTAMP_TIME) - return (null_value= 1); + set_func_handler(sign > 0 ? &func_handler_add_time_time_add : + &func_handler_add_time_time_sub); } else { - // ADDTIME function AND the first argument is TIME - if (args[0]->get_time(&l_time1) || - args[1]->get_time(&l_time2) || - l_time2.time_type != MYSQL_TIMESTAMP_TIME) - return (null_value= 1); - is_time= (l_time1.time_type == MYSQL_TIMESTAMP_TIME); + set_func_handler(sign > 0 ? &func_handler_add_time_string_add : + &func_handler_add_time_string_sub); } - if (l_time1.neg != l_time2.neg) - l_sign= -l_sign; - - bzero(ltime, sizeof(*ltime)); - - ltime->neg= calc_time_diff(&l_time1, &l_time2, -l_sign, - &seconds, µseconds); - - /* - If first argument was negative and diff between arguments - is non-zero we need to swap sign to get proper result. - */ - if (l_time1.neg && (seconds || microseconds)) - ltime->neg= 1-ltime->neg; // Swap sign of result - - if (!is_time && ltime->neg) - return (null_value= 1); - - days= (long) (seconds / SECONDS_IN_24H); - - calc_time_from_sec(ltime, (long)(seconds % SECONDS_IN_24H), microseconds); - ltime->time_type= is_time ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATETIME; - - if (!is_time) - { - if (get_date_from_daynr(days,<ime->year,<ime->month,<ime->day) || - !ltime->day) - return (null_value= 1); - return (null_value= 0); - } - - ltime->hour+= days*24; - return (null_value= adjust_time_range_with_warn(ltime, decimals)); + maybe_null= true; + return m_func_handler->fix_length_and_dec(this); } @@ -2826,7 +2530,7 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) Result: Time value */ -bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_timediff::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); int l_sign= 1; @@ -2834,22 +2538,22 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) ErrConvTime str(&l_time3); /* the following may be true in, for example, date_add(timediff(...), ... */ - if (fuzzy_date & TIME_NO_ZERO_IN_DATE) + if (fuzzydate & TIME_NO_ZERO_IN_DATE) return (null_value= 1); - if (args[0]->get_time(&l_time1) || - args[1]->get_time(&l_time2) || + if (args[0]->get_time(thd, &l_time1) || + args[1]->get_time(thd, &l_time2) || l_time1.time_type != l_time2.time_type) return (null_value= 1); if (l_time1.neg != l_time2.neg) l_sign= -l_sign; - if (calc_time_diff(&l_time1, &l_time2, l_sign, &l_time3, fuzzy_date)) + if (calc_time_diff(&l_time1, &l_time2, l_sign, &l_time3, fuzzydate)) return (null_value= 1); *ltime= l_time3; - return (null_value= adjust_time_range_with_warn(ltime, decimals)); + return (null_value= adjust_time_range_with_warn(thd, ltime, decimals)); } /** @@ -2858,17 +2562,16 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) Result: Time value */ -bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_maketime::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); Longlong_hybrid hour(args[0]->val_int(), args[0]->unsigned_flag); longlong minute= args[1]->val_int(); - ulonglong second; - ulong microsecond; - bool neg= args[2]->get_seconds(&second, µsecond); + VSec6 sec(thd, args[2], "seconds", 59); - if (args[0]->null_value || args[1]->null_value || args[2]->null_value || - minute < 0 || minute > 59 || neg || second > 59) + DBUG_ASSERT(sec.sec() <= 59); + if (args[0]->null_value || args[1]->null_value || sec.is_null() || + minute < 0 || minute > 59 || sec.neg() || sec.truncated()) return (null_value= 1); bzero(ltime, sizeof(*ltime)); @@ -2879,8 +2582,8 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { ltime->hour= (uint) hour.abs(); ltime->minute= (uint) minute; - ltime->second= (uint) second; - ltime->second_part= microsecond; + ltime->second= (uint) sec.sec(); + ltime->second_part= sec.usec(); } else { @@ -2890,10 +2593,10 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) check_time_range(ltime, decimals, &unused); char buf[28]; char *ptr= longlong10_to_str(hour.value(), buf, hour.is_unsigned() ? 10 : -10); - int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u", (uint)minute, (uint)second); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - buf, len, MYSQL_TIMESTAMP_TIME, - NullS); + int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u", + (uint) minute, (uint) sec.sec()); + ErrConvString err(buf, len, &my_charset_bin); + thd->push_warning_truncated_wrong_value("time", err.ptr()); } return (null_value= 0); @@ -2911,7 +2614,7 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) longlong Item_func_microsecond::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return ((null_value= !tm.is_valid_time())) ? 0 : tm.get_mysql_time()->second_part; } @@ -2920,12 +2623,12 @@ longlong Item_func_microsecond::val_int() longlong Item_func_timestamp_diff::val_int() { MYSQL_TIME ltime1, ltime2; - longlong seconds; - long microseconds; + ulonglong seconds; + ulong microseconds; long months= 0; int neg= 1; THD *thd= current_thd; - ulonglong fuzzydate= TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE; + date_mode_t fuzzydate= TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE; null_value= 0; @@ -3000,21 +2703,21 @@ longlong Item_func_timestamp_diff::val_int() case INTERVAL_MONTH: return months*neg; case INTERVAL_WEEK: - return seconds / SECONDS_IN_24H / 7L * neg; + return ((longlong) (seconds / SECONDS_IN_24H / 7L)) * neg; case INTERVAL_DAY: - return seconds / SECONDS_IN_24H * neg; + return ((longlong) (seconds / SECONDS_IN_24H)) * neg; case INTERVAL_HOUR: - return seconds/3600L*neg; + return ((longlong) (seconds / 3600L)) * neg; case INTERVAL_MINUTE: - return seconds/60L*neg; + return ((longlong) (seconds / 60L)) * neg; case INTERVAL_SECOND: - return seconds*neg; + return ((longlong) seconds) * neg; case INTERVAL_MICROSECOND: /* In MySQL difference between any two valid datetime values in microseconds fits into longlong. */ - return (seconds*1000000L+microseconds)*neg; + return ((longlong) ((ulonglong) seconds * 1000000L + microseconds)) * neg; default: break; } @@ -3144,15 +2847,10 @@ void Item_func_get_format::print(String *str, enum_query_type query_type) specifiers supported by extract_date_time() function. @return - One of date_time_format_types values: - - DATE_TIME_MICROSECOND - - DATE_TIME - - DATE_ONLY - - TIME_MICROSECOND - - TIME_ONLY + A function handler corresponding the given format */ -static date_time_format_types +static const Item_handled_func::Handler * get_date_time_result_type(const char *format, uint length) { const char *time_part_frms= "HISThiklrs"; @@ -3179,21 +2877,21 @@ get_date_time_result_type(const char *format, uint length) frac_second_used implies time_part_used, and thus we already have all types of date-time components and can end our search. */ - return DATE_TIME_MICROSECOND; + return &func_handler_str_to_date_datetime_usec; } } } /* We don't have all three types of date-time components */ if (frac_second_used) - return TIME_MICROSECOND; + return &func_handler_str_to_date_time_usec; if (time_part_used) { if (date_part_used) - return DATE_TIME; - return TIME_ONLY; + return &func_handler_str_to_date_datetime_sec; + return &func_handler_str_to_date_time_sec; } - return DATE_ONLY; + return &func_handler_str_to_date_date; } @@ -3213,56 +2911,27 @@ bool Item_func_str_to_date::fix_length_and_dec() internal_charset= &my_charset_utf8mb4_general_ci; maybe_null= true; - set_handler(&type_handler_datetime2); - fix_attributes_datetime(TIME_SECOND_PART_DIGITS); + set_func_handler(&func_handler_str_to_date_datetime_usec); if ((const_item= args[1]->const_item())) { - char format_buff[64]; - String format_str(format_buff, sizeof(format_buff), &my_charset_bin); + StringBuffer<64> format_str; String *format= args[1]->val_str(&format_str, &format_converter, internal_charset); - decimals= 0; if (!args[1]->null_value) - { - date_time_format_types cached_format_type= - get_date_time_result_type(format->ptr(), format->length()); - switch (cached_format_type) { - case DATE_ONLY: - set_handler(&type_handler_newdate); - fix_attributes_date(); - break; - case TIME_MICROSECOND: - set_handler(&type_handler_time2); - fix_attributes_time(TIME_SECOND_PART_DIGITS); - break; - case TIME_ONLY: - set_handler(&type_handler_time2); - fix_attributes_time(0); - break; - case DATE_TIME_MICROSECOND: - set_handler(&type_handler_datetime2); - fix_attributes_datetime(TIME_SECOND_PART_DIGITS); - break; - case DATE_TIME: - set_handler(&type_handler_datetime2); - fix_attributes_datetime(0); - break; - } - } + set_func_handler(get_date_time_result_type(format->ptr(), format->length())); } - cached_timestamp_type= mysql_timestamp_type(); - return FALSE; + return m_func_handler->fix_length_and_dec(this); } -bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_str_to_date::get_date_common(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate, + timestamp_type tstype) { DATE_TIME_FORMAT date_time_format; - char val_buff[64], format_buff[64]; - String val_string(val_buff, sizeof(val_buff), &my_charset_bin), *val; - String format_str(format_buff, sizeof(format_buff), &my_charset_bin), - *format; + StringBuffer<64> val_string, format_str; + String *val, *format; val= args[0]->val_str(&val_string, &subject_converter, internal_charset); format= args[1]->val_str(&format_str, &format_converter, internal_charset); @@ -3271,29 +2940,19 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, ulonglong 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, cached_timestamp_type, 0, "datetime", - fuzzy_date | sql_mode_for_dates(current_thd))) + if (extract_date_time(thd, &date_time_format, val->ptr(), val->length(), + ltime, tstype, 0, "datetime", + fuzzydate | sql_mode_for_dates(thd))) return (null_value=1); - if (cached_timestamp_type == MYSQL_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 (null_value= 0); } -bool Item_func_last_day::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_last_day::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY) || - (ltime->month == 0)) - return (null_value=1); + Date *d= new(ltime) Date(thd, args[0], fuzzydate & ~TIME_TIME_ONLY); + if ((null_value= (!d->is_valid_date() || ltime->month == 0))) + return true; uint month_idx= ltime->month-1; ltime->day= days_in_month[month_idx]; if ( month_idx == 1 && calc_days_in_year(ltime->year) == 366) diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 7aacdec85e0..de5ba8df2fe 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -25,13 +25,9 @@ class MY_LOCALE; -enum date_time_format_types -{ - TIME_ONLY= 0, TIME_MICROSECOND, DATE_ONLY, DATE_TIME, DATE_TIME_MICROSECOND -}; - -bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval); +bool get_interval_value(THD *thd, Item *args, + interval_type int_type, INTERVAL *interval); class Item_long_func_date_field: public Item_long_func @@ -186,9 +182,9 @@ public: str->set(nr, collation.collation); return str; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return get_date_from_int(ltime, fuzzydate); + return get_date_from_int(thd, ltime, fuzzydate); } const char *func_name() const { return "month"; } const Type_handler *type_handler() const { return &type_handler_long; } @@ -459,9 +455,9 @@ public: { return (odbc_type ? "dayofweek" : "weekday"); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } const Type_handler *type_handler() const { return &type_handler_long; } bool fix_length_and_dec() @@ -516,7 +512,7 @@ public: } double real_op() { DBUG_ASSERT(0); return 0; } String *str_op(String *str) { DBUG_ASSERT(0); return 0; } - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(0); return true; @@ -552,7 +548,8 @@ public: } bool fix_length_and_dec() { - fix_length_and_dec_generic(arg_count ? args[0]->datetime_precision() : 0); + fix_length_and_dec_generic(arg_count ? + args[0]->datetime_precision(current_thd) : 0); return FALSE; } longlong int_op(); @@ -576,7 +573,7 @@ public: } bool fix_length_and_dec() { - fix_length_and_dec_generic(args[0]->time_precision()); + fix_length_and_dec_generic(args[0]->time_precision(current_thd)); return FALSE; } longlong int_op(); @@ -586,66 +583,17 @@ public: }; -class Item_temporal_func: public Item_func -{ -public: - Item_temporal_func(THD *thd): Item_func(thd) {} - Item_temporal_func(THD *thd, Item *a): Item_func(thd, a) {} - Item_temporal_func(THD *thd, Item *a, Item *b): Item_func(thd, a, b) {} - Item_temporal_func(THD *thd, Item *a, Item *b, Item *c): Item_func(thd, a, b, c) {} - String *val_str(String *str); - longlong val_int() { return val_int_from_date(); } - double val_real() { return val_real_from_date(); } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) { DBUG_ASSERT(0); return 1; } - my_decimal *val_decimal(my_decimal *decimal_value) - { return val_decimal_from_date(decimal_value); } -}; - - -/** - Abstract class for functions returning TIME, DATE, DATETIME or string values, - whose data type depends on parameters and is set at fix_fields time. -*/ -class Item_temporal_hybrid_func: public Item_hybrid_func -{ -protected: - String ascii_buf; // Conversion buffer -public: - Item_temporal_hybrid_func(THD *thd, Item *a, Item *b): - Item_hybrid_func(thd, a, b) {} - - longlong val_int() { return val_int_from_date(); } - double val_real() { return val_real_from_date(); } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date)= 0; - my_decimal *val_decimal(my_decimal *decimal_value) - { return val_decimal_from_date(decimal_value); } - - /** - Fix the returned timestamp to match field_type(), - which is important for val_str(). - */ - bool fix_temporal_type(MYSQL_TIME *ltime); - /** - Return string value in ASCII character set. - */ - String *val_str_ascii(String *str); - /** - Return string value in @@character_set_connection. - */ - String *val_str(String *str) - { - return val_str_from_val_str_ascii(str, &ascii_buf); - } -}; - - -class Item_datefunc :public Item_temporal_func +class Item_datefunc :public Item_func { public: - Item_datefunc(THD *thd): Item_temporal_func(thd) { } - Item_datefunc(THD *thd, Item *a): Item_temporal_func(thd, a) { } - Item_datefunc(THD *thd, Item *a, Item *b): Item_temporal_func(thd, a, b) { } + Item_datefunc(THD *thd): Item_func(thd) { } + Item_datefunc(THD *thd, Item *a): Item_func(thd, a) { } + Item_datefunc(THD *thd, Item *a, Item *b): Item_func(thd, a, b) { } const Type_handler *type_handler() const { return &type_handler_newdate; } + longlong val_int() { return Date(this).to_longlong(); } + double val_real() { return Date(this).to_double(); } + String *val_str(String *to) { return Date(this).to_string(to); } + my_decimal *val_decimal(my_decimal *to) { return Date(this).to_decimal(to); } bool fix_length_and_dec() { fix_attributes_date(); @@ -655,26 +603,34 @@ public: }; -class Item_timefunc :public Item_temporal_func +class Item_timefunc :public Item_func { public: - Item_timefunc(THD *thd): Item_temporal_func(thd) {} - Item_timefunc(THD *thd, Item *a): Item_temporal_func(thd, a) {} - Item_timefunc(THD *thd, Item *a, Item *b): Item_temporal_func(thd, a, b) {} - Item_timefunc(THD *thd, Item *a, Item *b, Item *c): - Item_temporal_func(thd, a, b ,c) {} + Item_timefunc(THD *thd): Item_func(thd) {} + Item_timefunc(THD *thd, Item *a): Item_func(thd, a) {} + Item_timefunc(THD *thd, Item *a, Item *b): Item_func(thd, a, b) {} + Item_timefunc(THD *thd, Item *a, Item *b, Item *c): Item_func(thd, a, b ,c) {} const Type_handler *type_handler() const { return &type_handler_time2; } + longlong val_int() { return Time(this).to_longlong(); } + double val_real() { return Time(this).to_double(); } + String *val_str(String *to) { return Time(this).to_string(to, decimals); } + my_decimal *val_decimal(my_decimal *to) { return Time(this).to_decimal(to); } }; -class Item_datetimefunc :public Item_temporal_func +class Item_datetimefunc :public Item_func { public: - Item_datetimefunc(THD *thd): Item_temporal_func(thd) {} - Item_datetimefunc(THD *thd, Item *a): Item_temporal_func(thd, a) {} + Item_datetimefunc(THD *thd): Item_func(thd) {} + Item_datetimefunc(THD *thd, Item *a): Item_func(thd, a) {} + Item_datetimefunc(THD *thd, Item *a, Item *b): Item_func(thd, a, b) {} Item_datetimefunc(THD *thd, Item *a, Item *b, Item *c): - Item_temporal_func(thd, a, b ,c) {} + Item_func(thd, a, b ,c) {} const Type_handler *type_handler() const { return &type_handler_datetime2; } + longlong val_int() { return Datetime(this).to_longlong(); } + double val_real() { return Datetime(this).to_double(); } + String *val_str(String *to) { return Datetime(this).to_string(to, decimals); } + my_decimal *val_decimal(my_decimal *to) { return Datetime(this).to_decimal(to); } }; @@ -689,7 +645,7 @@ public: { decimals= dec; } bool fix_fields(THD *, Item **); bool fix_length_and_dec() { fix_attributes_time(decimals); return FALSE; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); /* Abstract method that defines which time zone is used for conversion. Converts time current time in my_time_t representation to broken-down @@ -734,7 +690,7 @@ class Item_func_curdate :public Item_datefunc MYSQL_TIME ltime; public: Item_func_curdate(THD *thd): Item_datefunc(thd), last_query_id(0) {} - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0; bool check_vcol_func_processor(void *arg) { @@ -777,7 +733,7 @@ public: bool fix_fields(THD *, Item **); bool fix_length_and_dec() { fix_attributes_datetime(decimals); return FALSE;} - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0; bool check_vcol_func_processor(void *arg) { @@ -832,7 +788,7 @@ public: bool const_item() const { return 0; } const char *func_name() const { return "sysdate"; } void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); table_map used_tables() const { return RAND_TABLE_BIT; } bool check_vcol_func_processor(void *arg) { @@ -852,7 +808,7 @@ class Item_func_from_days :public Item_datefunc public: Item_func_from_days(THD *thd, Item *a): Item_datefunc(thd, a) {} const char *func_name() const { return "from_days"; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} bool check_valid_arguments_processor(void *int_arg) @@ -917,7 +873,7 @@ class Item_func_from_unixtime :public Item_datetimefunc Item_func_from_unixtime(THD *thd, Item *a): Item_datetimefunc(thd, a) {} const char *func_name() const { return "from_unixtime"; } bool fix_length_and_dec(); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_from_unixtime>(thd, this); } }; @@ -958,11 +914,11 @@ class Item_func_convert_tz :public Item_datetimefunc const char *func_name() const { return "convert_tz"; } bool fix_length_and_dec() { - fix_attributes_datetime(args[0]->datetime_precision()); + fix_attributes_datetime(args[0]->datetime_precision(current_thd)); maybe_null= true; return FALSE; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); void cleanup(); Item *get_copy(THD *thd) { return get_item_copy<Item_func_convert_tz>(thd, this); } @@ -975,7 +931,7 @@ class Item_func_sec_to_time :public Item_timefunc { return args[0]->check_type_can_return_decimal(func_name()); } public: Item_func_sec_to_time(THD *thd, Item *item): Item_timefunc(thd, item) {} - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); bool fix_length_and_dec() { fix_attributes_time(args[0]->decimals); @@ -988,18 +944,17 @@ public: }; -class Item_date_add_interval :public Item_temporal_hybrid_func +class Item_date_add_interval :public Item_handled_func { public: const interval_type int_type; // keep it public const bool date_sub_interval; // keep it public Item_date_add_interval(THD *thd, Item *a, Item *b, interval_type type_arg, bool neg_arg): - Item_temporal_hybrid_func(thd, a, b),int_type(type_arg), + Item_handled_func(thd, a, b), int_type(type_arg), date_sub_interval(neg_arg) {} const char *func_name() const { return "date_add_interval"; } bool fix_length_and_dec(); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); bool eq(const Item *item, bool binary_cmp) const; void print(String *str, enum_query_type query_type); enum precedence precedence() const { return ADDINTERVAL_PRECEDENCE; } @@ -1009,9 +964,17 @@ public: }; -class Item_extract :public Item_int_func +class Item_extract :public Item_int_func, + public Type_handler_hybrid_field_type { - bool date_value; + date_mode_t m_date_mode; + const Type_handler_int_result *handler_by_length(uint32 length, + uint32 threashold) + { + if (length >= threashold) + return &type_handler_longlong; + return &type_handler_long; + } void set_date_length(uint32 length) { /* @@ -1020,48 +983,34 @@ class Item_extract :public Item_int_func because all around the code we assume that max_length is sign inclusive. Another options is to set unsigned_flag to "true". */ - max_length= length; //QQ: see above - date_value= true; + set_handler(handler_by_length(max_length= length, 10)); // QQ: see above + m_date_mode= date_mode_t(0); + } + void set_day_length(uint32 length) + { + /* + Units starting with DAY can be negative: + EXTRACT(DAY FROM '-24:00:00') -> -1 + */ + set_handler(handler_by_length(max_length= length + 1/*sign*/, 11)); + m_date_mode= date_mode_t(0); } void set_time_length(uint32 length) { - max_length= length + 1/*sign*/; - date_value= false; + set_handler(handler_by_length(max_length= length + 1/*sign*/, 11)); + m_date_mode= TIME_TIME_ONLY; } public: const interval_type int_type; // keep it public Item_extract(THD *thd, interval_type type_arg, Item *a): - Item_int_func(thd, a), int_type(type_arg) {} + Item_int_func(thd, a), + Type_handler_hybrid_field_type(&type_handler_longlong), + m_date_mode(date_mode_t(0)), + int_type(type_arg) + { } const Type_handler *type_handler() const { - switch (int_type) { - case INTERVAL_YEAR: - case INTERVAL_YEAR_MONTH: - case INTERVAL_QUARTER: - case INTERVAL_MONTH: - case INTERVAL_WEEK: - case INTERVAL_DAY: - case INTERVAL_DAY_HOUR: - case INTERVAL_DAY_MINUTE: - case INTERVAL_DAY_SECOND: - case INTERVAL_HOUR: - case INTERVAL_HOUR_MINUTE: - case INTERVAL_HOUR_SECOND: - case INTERVAL_MINUTE: - case INTERVAL_MINUTE_SECOND: - case INTERVAL_SECOND: - case INTERVAL_MICROSECOND: - case INTERVAL_SECOND_MICROSECOND: - return &type_handler_long; - case INTERVAL_DAY_MICROSECOND: - case INTERVAL_HOUR_MICROSECOND: - case INTERVAL_MINUTE_MICROSECOND: - return &type_handler_longlong; - case INTERVAL_LAST: - break; - } - DBUG_ASSERT(0); - return &type_handler_longlong; + return Type_handler_hybrid_field_type::type_handler(); } longlong val_int(); enum Functype functype() const { return EXTRACT_FUNC; } @@ -1157,22 +1106,16 @@ public: }; -class Item_temporal_typecast: public Item_temporal_func +class Item_date_typecast :public Item_datefunc { public: - Item_temporal_typecast(THD *thd, Item *a): Item_temporal_func(thd, a) {} - virtual const char *cast_type() const = 0; - void print(String *str, enum_query_type query_type); -}; - -class Item_date_typecast :public Item_temporal_typecast -{ -public: - Item_date_typecast(THD *thd, Item *a): Item_temporal_typecast(thd, a) {} + Item_date_typecast(THD *thd, Item *a): Item_datefunc(thd, a) {} const char *func_name() const { return "cast_as_date"; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); - const char *cast_type() const { return "date"; } - const Type_handler *type_handler() const { return &type_handler_newdate; } + void print(String *str, enum_query_type query_type) + { + print_cast_temporal(str, query_type); + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool fix_length_and_dec() { return args[0]->type_handler()->Item_date_typecast_fix_length_and_dec(this); @@ -1182,15 +1125,17 @@ public: }; -class Item_time_typecast :public Item_temporal_typecast +class Item_time_typecast :public Item_timefunc { public: Item_time_typecast(THD *thd, Item *a, uint dec_arg): - Item_temporal_typecast(thd, a) { decimals= dec_arg; } + Item_timefunc(thd, a) { decimals= dec_arg; } const char *func_name() const { return "cast_as_time"; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); - const char *cast_type() const { return "time"; } - const Type_handler *type_handler() const { return &type_handler_time2; } + void print(String *str, enum_query_type query_type) + { + print_cast_temporal(str, query_type); + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool fix_length_and_dec() { return args[0]->type_handler()-> @@ -1201,15 +1146,17 @@ public: }; -class Item_datetime_typecast :public Item_temporal_typecast +class Item_datetime_typecast :public Item_datetimefunc { public: Item_datetime_typecast(THD *thd, Item *a, uint dec_arg): - Item_temporal_typecast(thd, a) { decimals= dec_arg; } + Item_datetimefunc(thd, a) { decimals= dec_arg; } const char *func_name() const { return "cast_as_datetime"; } - const char *cast_type() const { return "datetime"; } - const Type_handler *type_handler() const { return &type_handler_datetime2; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + void print(String *str, enum_query_type query_type) + { + print_cast_temporal(str, query_type); + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool fix_length_and_dec() { return args[0]->type_handler()-> @@ -1228,31 +1175,73 @@ public: Item_func_makedate(THD *thd, Item *a, Item *b): Item_datefunc(thd, a, b) {} const char *func_name() const { return "makedate"; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_makedate>(thd, this); } }; -class Item_func_add_time :public Item_temporal_hybrid_func +class Item_func_timestamp :public Item_datetimefunc { - const bool is_date; - int sign; - + bool check_arguments() const + { + return args[0]->check_type_can_return_date(func_name()) || + args[1]->check_type_can_return_time(func_name()); + } public: - Item_func_add_time(THD *thd, Item *a, Item *b, bool type_arg, bool neg_arg): - Item_temporal_hybrid_func(thd, a, b), is_date(type_arg) - { sign= neg_arg ? -1 : 1; } - bool fix_length_and_dec(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); - const char *func_name() const + Item_func_timestamp(THD *thd, Item *a, Item *b) + :Item_datetimefunc(thd, a, b) + { } + const char *func_name() const { return "timestamp"; } + bool fix_length_and_dec() + { + THD *thd= current_thd; + uint dec0= args[0]->datetime_precision(thd); + uint dec1= Interval_DDhhmmssff::fsp(thd, args[1]); + fix_attributes_datetime(MY_MAX(dec0, dec1)); + maybe_null= true; + return false; + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return is_date ? "timestamp" : sign > 0 ? "addtime" : "subtime"; + Datetime dt(thd, args[0], date_mode_t(0)); + if (!dt.is_valid_datetime()) + return null_value= true; + Interval_DDhhmmssff it(thd, args[1]); + if (!it.is_valid_interval_DDhhmmssff()) + return null_value= true; + return (null_value= Sec6_add(dt.get_mysql_time(), it.get_mysql_time(), 1). + to_datetime(ltime)); } Item *get_copy(THD *thd) + { return get_item_copy<Item_func_timestamp>(thd, this); } +}; + + +/** + ADDTIME(t,a) and SUBTIME(t,a) are time functions that calculate a + time/datetime value + + t: time_or_datetime_expression + a: time_expression + + Result: Time value or datetime value +*/ + +class Item_func_add_time :public Item_handled_func +{ + int sign; +public: + Item_func_add_time(THD *thd, Item *a, Item *b, bool neg_arg) + :Item_handled_func(thd, a, b), sign(neg_arg ? -1 : 1) + { } + bool fix_length_and_dec(); + const char *func_name() const { return sign > 0 ? "addtime" : "subtime"; } + Item *get_copy(THD *thd) { return get_item_copy<Item_func_add_time>(thd, this); } }; + class Item_func_timediff :public Item_timefunc { bool check_arguments() const @@ -1262,12 +1251,14 @@ public: const char *func_name() const { return "timediff"; } bool fix_length_and_dec() { - uint dec= MY_MAX(args[0]->time_precision(), args[1]->time_precision()); + THD *thd= current_thd; + uint dec= MY_MAX(args[0]->time_precision(thd), + args[1]->time_precision(thd)); fix_attributes_time(dec); maybe_null= true; return FALSE; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_timediff>(thd, this); } }; @@ -1290,7 +1281,7 @@ public: return FALSE; } const char *func_name() const { return "maketime"; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_maketime>(thd, this); } }; @@ -1369,19 +1360,19 @@ public: }; -class Item_func_str_to_date :public Item_temporal_hybrid_func +class Item_func_str_to_date :public Item_handled_func { - timestamp_type cached_timestamp_type; bool const_item; String subject_converter; String format_converter; CHARSET_INFO *internal_charset; public: Item_func_str_to_date(THD *thd, Item *a, Item *b): - Item_temporal_hybrid_func(thd, a, b), const_item(false), + Item_handled_func(thd, a, b), const_item(false), internal_charset(NULL) {} - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date_common(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate, + timestamp_type); const char *func_name() const { return "str_to_date"; } bool fix_length_and_dec(); Item *get_copy(THD *thd) @@ -1396,9 +1387,358 @@ class Item_func_last_day :public Item_datefunc public: Item_func_last_day(THD *thd, Item *a): Item_datefunc(thd, a) {} const char *func_name() const { return "last_day"; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_last_day>(thd, this); } }; + +/*****************************************************************************/ + +class Func_handler_date_add_interval +{ +protected: + static uint interval_dec(const Item *item, interval_type int_type) + { + if (int_type == INTERVAL_MICROSECOND || + (int_type >= INTERVAL_DAY_MICROSECOND && + int_type <= INTERVAL_SECOND_MICROSECOND)) + return TIME_SECOND_PART_DIGITS; + if (int_type == INTERVAL_SECOND && item->decimals > 0) + return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); + return 0; + } + interval_type int_type(const Item_handled_func *item) const + { + return static_cast<const Item_date_add_interval*>(item)->int_type; + } + bool sub(const Item_handled_func *item) const + { + return static_cast<const Item_date_add_interval*>(item)->date_sub_interval; + } + bool add(THD *thd, Item *item, interval_type type, bool sub, MYSQL_TIME *to) const + { + INTERVAL interval; + if (get_interval_value(thd, item, type, &interval)) + return true; + if (sub) + interval.neg = !interval.neg; + return date_add_interval(thd, to, type, interval); + } +}; + + +class Func_handler_date_add_interval_datetime: + public Item_handled_func::Handler_datetime, + public Func_handler_date_add_interval +{ +public: + bool fix_length_and_dec(Item_handled_func *item) const + { + uint dec= MY_MAX(item->arguments()[0]->datetime_precision(current_thd), + interval_dec(item->arguments()[1], int_type(item))); + item->fix_attributes_datetime(dec); + return false; + } + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + Datetime dt(thd, item->arguments()[0], date_mode_t(0)); + if (!dt.is_valid_datetime() || + dt.check_date_with_warn(thd, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) + return (item->null_value= true); + dt.copy_to_mysql_time(to); + return (item->null_value= add(thd, item->arguments()[1], + int_type(item), sub(item), to)); + } +}; + + +class Func_handler_date_add_interval_datetime_arg0_time: + public Func_handler_date_add_interval_datetime +{ +public: + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const; +}; + + +class Func_handler_date_add_interval_date: + public Item_handled_func::Handler_date, + public Func_handler_date_add_interval +{ +public: + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + Date d(thd, item->arguments()[0], date_mode_t(0)); + if (!d.is_valid_date() || + d.check_date_with_warn(thd, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) + return (item->null_value= true); + d.copy_to_mysql_time(to); + return (item->null_value= add(thd, item->arguments()[1], + int_type(item), sub(item), to)); + } +}; + + +class Func_handler_date_add_interval_time: + public Item_handled_func::Handler_time, + public Func_handler_date_add_interval +{ +public: + bool fix_length_and_dec(Item_handled_func *item) const + { + uint dec= MY_MAX(item->arguments()[0]->time_precision(current_thd), + interval_dec(item->arguments()[1], int_type(item))); + item->fix_attributes_time(dec); + return false; + } + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + Time t(thd, item->arguments()[0]); + if (!t.is_valid_time()) + return (item->null_value= true); + t.copy_to_mysql_time(to); + return (item->null_value= add(thd, item->arguments()[1], + int_type(item), sub(item), to)); + } +}; + + +class Func_handler_date_add_interval_string: + public Item_handled_func::Handler_temporal_string, + public Func_handler_date_add_interval +{ +public: + bool fix_length_and_dec(Item_handled_func *item) const + { + uint dec= MY_MAX(item->arguments()[0]->datetime_precision(current_thd), + interval_dec(item->arguments()[1], int_type(item))); + item->collation.set(item->default_charset(), + DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); + item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); + return false; + } + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + if (item->arguments()[0]->get_date(thd, to, date_mode_t(0)) || + (to->time_type != MYSQL_TIMESTAMP_TIME && + check_date_with_warn(thd, to, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE, + MYSQL_TIMESTAMP_ERROR))) + return (item->null_value= true); + return (item->null_value= add(thd, item->arguments()[1], + int_type(item), sub(item), to)); + } +}; + + +class Func_handler_sign +{ +protected: + int m_sign; + Func_handler_sign(int sign) :m_sign(sign) { } +}; + + +class Func_handler_add_time_datetime: + public Item_handled_func::Handler_datetime, + public Func_handler_sign +{ +public: + Func_handler_add_time_datetime(int sign) + :Func_handler_sign(sign) + { } + bool fix_length_and_dec(Item_handled_func *item) const + { + THD *thd= current_thd; + uint dec0= item->arguments()[0]->datetime_precision(thd); + uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]); + item->fix_attributes_datetime(MY_MAX(dec0, dec1)); + return false; + } + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + DBUG_ASSERT(item->is_fixed()); + Datetime dt(thd, item->arguments()[0], date_mode_t(0)); + if (!dt.is_valid_datetime()) + return item->null_value= true; + Interval_DDhhmmssff it(thd, item->arguments()[1]); + if (!it.is_valid_interval_DDhhmmssff()) + return item->null_value= true; + return (item->null_value= (Sec6_add(dt.get_mysql_time(), + it.get_mysql_time(), m_sign). + to_datetime(to))); + } +}; + + +class Func_handler_add_time_time: + public Item_handled_func::Handler_time, + public Func_handler_sign +{ +public: + Func_handler_add_time_time(int sign) + :Func_handler_sign(sign) + { } + bool fix_length_and_dec(Item_handled_func *item) const + { + THD *thd= current_thd; + uint dec0= item->arguments()[0]->time_precision(thd); + uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]); + item->fix_attributes_time(MY_MAX(dec0, dec1)); + return false; + } + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + DBUG_ASSERT(item->is_fixed()); + Time t(thd, item->arguments()[0]); + if (!t.is_valid_time()) + return item->null_value= true; + Interval_DDhhmmssff i(thd, item->arguments()[1]); + if (!i.is_valid_interval_DDhhmmssff()) + return item->null_value= true; + return (item->null_value= (Sec6_add(t.get_mysql_time(), + i.get_mysql_time(), m_sign). + to_time(thd, to, item->decimals))); + } +}; + + +class Func_handler_add_time_string: + public Item_handled_func::Handler_temporal_string, + public Func_handler_sign +{ +public: + Func_handler_add_time_string(int sign) + :Func_handler_sign(sign) + { } + bool fix_length_and_dec(Item_handled_func *item) const + { + uint dec0= item->arguments()[0]->decimals; + uint dec1= Interval_DDhhmmssff::fsp(current_thd, item->arguments()[1]); + uint dec= MY_MAX(dec0, dec1); + item->collation.set(item->default_charset(), + DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); + item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); + return false; + } + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + DBUG_ASSERT(item->is_fixed()); + // Detect a proper timestamp type based on the argument values + Temporal_hybrid l_time1(thd, item->arguments()[0], TIME_TIME_ONLY); + if (!l_time1.is_valid_temporal()) + return (item->null_value= true); + Interval_DDhhmmssff l_time2(thd, item->arguments()[1]); + if (!l_time2.is_valid_interval_DDhhmmssff()) + return (item->null_value= true); + Sec6_add add(l_time1.get_mysql_time(), l_time2.get_mysql_time(), m_sign); + return (item->null_value= (l_time1.get_mysql_time()->time_type == + MYSQL_TIMESTAMP_TIME ? + add.to_time(thd, to, item->decimals) : + add.to_datetime(to))); + } +}; + + +class Func_handler_str_to_date_datetime_sec: + public Item_handled_func::Handler_datetime +{ +public: + bool fix_length_and_dec(Item_handled_func *item) const + { + item->fix_attributes_datetime(0); + return false; + } + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + return static_cast<Item_func_str_to_date*>(item)-> + get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_DATETIME); + } +}; + + +class Func_handler_str_to_date_datetime_usec: + public Item_handled_func::Handler_datetime +{ +public: + bool fix_length_and_dec(Item_handled_func *item) const + { + item->fix_attributes_datetime(TIME_SECOND_PART_DIGITS); + return false; + } + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + return static_cast<Item_func_str_to_date*>(item)-> + get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_DATETIME); + } +}; + + +class Func_handler_str_to_date_date: public Item_handled_func::Handler_date +{ +public: + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + return static_cast<Item_func_str_to_date*>(item)-> + get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_DATE); + } +}; + + +class Func_handler_str_to_date_time: public Item_handled_func::Handler_time +{ +public: + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const + { + if (static_cast<Item_func_str_to_date*>(item)-> + get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_TIME)) + return true; + if (to->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. + */ + to->hour+= to->day * 24; + to->day= 0; + } + return false; + } +}; + + +class Func_handler_str_to_date_time_sec: public Func_handler_str_to_date_time +{ +public: + bool fix_length_and_dec(Item_handled_func *item) const + { + item->fix_attributes_time(0); + return false; + } +}; + + +class Func_handler_str_to_date_time_usec: public Func_handler_str_to_date_time +{ +public: + bool fix_length_and_dec(Item_handled_func *item) const + { + item->fix_attributes_time(TIME_SECOND_PART_DIGITS); + return false; + } +}; + + #endif /* ITEM_TIMEFUNC_INCLUDED */ diff --git a/sql/item_vers.cc b/sql/item_vers.cc index d7361f687f9..6946ae0e1e5 100644 --- a/sql/item_vers.cc +++ b/sql/item_vers.cc @@ -37,9 +37,8 @@ Item_func_trt_ts::Item_func_trt_ts(THD *thd, Item* a, TR_table::field_id_t _trt_ bool -Item_func_trt_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) +Item_func_trt_ts::get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate) { - THD *thd= current_thd; // can it differ from constructor's? DBUG_ASSERT(thd); DBUG_ASSERT(args[0]); if (args[0]->result_type() != INT_RESULT) @@ -67,7 +66,7 @@ Item_func_trt_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) return true; } - return trt[trt_field]->get_date(res, fuzzy_date); + return trt[trt_field]->get_date(res, fuzzydate); } @@ -143,7 +142,7 @@ Item_func_trt_id::val_int() else { MYSQL_TIME commit_ts; - if (args[0]->get_date(&commit_ts, 0)) + if (args[0]->get_date(current_thd, &commit_ts, date_mode_t(0))) { null_value= true; return 0; diff --git a/sql/item_vers.h b/sql/item_vers.h index 8b9c0e6056c..a42b5a033f2 100644 --- a/sql/item_vers.h +++ b/sql/item_vers.h @@ -35,7 +35,7 @@ public: } return "trt_commit_ts"; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_trt_ts>(thd, this); } bool fix_length_and_dec() diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc index 2db396d3065..aa2c7756ab7 100644 --- a/sql/item_windowfunc.cc +++ b/sql/item_windowfunc.cc @@ -120,7 +120,6 @@ Item_window_func::fix_fields(THD *thd, Item **ref) const_item_cache= false; with_window_func= true; - with_sum_func= false; if (fix_length_and_dec()) return TRUE; @@ -440,12 +439,12 @@ Item_sum_hybrid_simple::val_str(String *str) return retval; } -bool Item_sum_hybrid_simple::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_sum_hybrid_simple::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); if (null_value) return true; - bool retval= value->get_date(ltime, fuzzydate); + bool retval= value->get_date(thd, ltime, fuzzydate); if ((null_value= value->null_value)) DBUG_ASSERT(retval == true); return retval; @@ -514,11 +513,11 @@ void Item_sum_hybrid_simple::reset_field() } case DECIMAL_RESULT: { - my_decimal value_buff, *arg_dec= args[0]->val_decimal(&value_buff); + VDec arg_dec(args[0]); if (maybe_null) { - if (args[0]->null_value) + if (arg_dec.is_null()) result_field->set_null(); else result_field->set_notnull(); @@ -527,9 +526,7 @@ void Item_sum_hybrid_simple::reset_field() We must store zero in the field as we will use the field value in add() */ - if (!arg_dec) // Null - arg_dec= &decimal_zero; - result_field->store_decimal(arg_dec); + result_field->store_decimal(arg_dec.ptr_or(&decimal_zero)); break; } case ROW_RESULT: diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h index 9ba60c3956d..4c704808fe4 100644 --- a/sql/item_windowfunc.h +++ b/sql/item_windowfunc.h @@ -319,7 +319,7 @@ class Item_sum_hybrid_simple : public Item_sum, my_decimal *val_decimal(my_decimal *); void reset_field(); String *val_str(String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); const Type_handler *type_handler() const { return Type_handler_hybrid_field_type::type_handler(); } void update_field(); @@ -1273,7 +1273,7 @@ public: return res; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { bool res; if (force_return_blank) @@ -1290,7 +1290,7 @@ public: } else { - res= window_func()->get_date(ltime, fuzzydate); + res= window_func()->get_date(thd, ltime, fuzzydate); null_value= window_func()->null_value; } return res; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 63734ecf9ac..146c5aa57fe 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -29,10 +29,8 @@ /* TODO: future development directions: - 1. add real constants for XPATH_NODESET_CMP and XPATH_NODESET - into enum Type in item.h. - 2. add nodeset_to_nodeset_comparator - 3. add lacking functions: + 1. add nodeset_to_nodeset_comparator + 2. add lacking functions: - name() - lang() - string() @@ -44,7 +42,7 @@ - substring-after() - normalize-space() - substring-before() - 4. add lacking axis: + 3. add lacking axis: - following-sibling - following, - preceding-sibling @@ -151,6 +149,9 @@ public: }; +static Type_handler_long_blob type_handler_xpath_nodeset; + + /* Common features of the functions returning a node set. */ @@ -181,16 +182,29 @@ public: void prepare(String *nodeset) { prepare_nodes(); - String *res= args[0]->val_nodeset(&tmp_value); + String *res= args[0]->val_raw(&tmp_value); fltbeg= (MY_XPATH_FLT*) res->ptr(); fltend= (MY_XPATH_FLT*) (res->ptr() + res->length()); nodeset->length(0); } - enum Type type() const { return XPATH_NODESET; } + const Type_handler *type_handler() const + { + return &type_handler_xpath_nodeset; + } + const Type_handler *fixed_type_handler() const + { + return &type_handler_xpath_nodeset; + } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + DBUG_ASSERT(0); + return NULL; + } String *val_str(String *str) { prepare_nodes(); - String *res= val_nodeset(&tmp2_value); + String *res= val_raw(&tmp2_value); fltbeg= (MY_XPATH_FLT*) res->ptr(); fltend= (MY_XPATH_FLT*) (res->ptr() + res->length()); String active; @@ -247,7 +261,7 @@ public: Item_nodeset_func_rootelement(THD *thd, String *pxml): Item_nodeset_func(thd, pxml) {} const char *func_name() const { return "xpath_rootelement"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_rootelement>(thd, this); } }; @@ -260,7 +274,7 @@ public: Item_nodeset_func_union(THD *thd, Item *a, Item *b, String *pxml): Item_nodeset_func(thd, a, b, pxml) {} const char *func_name() const { return "xpath_union"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_union>(thd, this); } }; @@ -294,7 +308,7 @@ public: String *pxml): Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_selfbyname"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_selfbyname>(thd, this); } }; @@ -308,7 +322,7 @@ public: String *pxml): Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_childbyname"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_childbyname>(thd, this); } }; @@ -324,7 +338,7 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml), need_self(need_self_arg) {} const char *func_name() const { return "xpath_descendantbyname"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_descendantbyname>(thd, this); } }; @@ -340,7 +354,7 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml), need_self(need_self_arg) {} const char *func_name() const { return "xpath_ancestorbyname"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, this); } }; @@ -354,7 +368,7 @@ public: String *pxml): Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_parentbyname"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_parentbyname>(thd, this); } }; @@ -368,7 +382,7 @@ public: uint l_arg, String *pxml): Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_attributebyname"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_attributebyname>(thd, this); } }; @@ -385,7 +399,7 @@ public: Item_nodeset_func_predicate(THD *thd, Item *a, Item *b, String *pxml): Item_nodeset_func(thd, a, b, pxml) {} const char *func_name() const { return "xpath_predicate"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_predicate>(thd, this); } }; @@ -398,7 +412,7 @@ public: Item_nodeset_func_elementbyindex(THD *thd, Item *a, Item *b, String *pxml): Item_nodeset_func(thd, a, b, pxml) { } const char *func_name() const { return "xpath_elementbyindex"; } - String *val_nodeset(String *nodeset); + String *val_raw(String *nodeset); Item *get_copy(THD *thd) { return get_item_copy<Item_nodeset_func_elementbyindex>(thd, this); } }; @@ -420,9 +434,9 @@ public: const char *func_name() const { return "xpath_cast_bool"; } longlong val_int() { - if (args[0]->type() == XPATH_NODESET) + if (args[0]->fixed_type_handler() == &type_handler_xpath_nodeset) { - String *flt= args[0]->val_nodeset(&tmp_value); + String *flt= args[0]->val_raw(&tmp_value); return flt->length() == sizeof(MY_XPATH_FLT) ? 1 : 0; } return args[0]->val_real() ? 1 : 0; @@ -455,7 +469,7 @@ public: String *string_cache; Item_nodeset_context_cache(THD *thd, String *str_arg, String *pxml): Item_nodeset_func(thd, pxml), string_cache(str_arg) { } - String *val_nodeset(String *res) + String *val_raw(String *res) { return string_cache; } bool fix_length_and_dec() { max_length= MAX_BLOB_WIDTH;; return FALSE; } Item *get_copy(THD *thd) @@ -474,7 +488,7 @@ public: bool fix_length_and_dec() { max_length=10; return FALSE; } longlong val_int() { - String *flt= args[0]->val_nodeset(&tmp_value); + String *flt= args[0]->val_raw(&tmp_value); if (flt->length() == sizeof(MY_XPATH_FLT)) return ((MY_XPATH_FLT*)flt->ptr())->pos + 1; return 0; @@ -496,7 +510,7 @@ public: longlong val_int() { uint predicate_supplied_context_size; - String *res= args[0]->val_nodeset(&tmp_value); + String *res= args[0]->val_raw(&tmp_value); if (res->length() == sizeof(MY_XPATH_FLT) && (predicate_supplied_context_size= ((MY_XPATH_FLT*)res->ptr())->size)) return predicate_supplied_context_size; @@ -519,7 +533,7 @@ public: double val_real() { double sum= 0; - String *res= args[0]->val_nodeset(&tmp_value); + String *res= args[0]->val_raw(&tmp_value); MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr(); MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length()); uint numnodes= pxml->length() / sizeof(MY_XML_NODE); @@ -587,19 +601,23 @@ public: Item_nodeset_to_const_comparator(THD *thd, Item *nodeset, Item *cmpfunc, String *p): Item_bool_func(thd, nodeset, cmpfunc), pxml(p) {} - enum Type type() const { return XPATH_NODESET_CMP; }; const char *func_name() const { return "xpath_nodeset_to_const_comparator"; } bool check_vcol_func_processor(void *arg) { return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE); } - + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + DBUG_ASSERT(0); + return NULL; + } longlong val_int() { Item_func *comp= (Item_func*)args[1]; Item_string_xml_non_const *fake= (Item_string_xml_non_const*)(comp->arguments()[0]); - String *res= args[0]->val_nodeset(&tmp_nodeset); + String *res= args[0]->val_raw(&tmp_nodeset); MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr(); MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length()); MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml->ptr(); @@ -630,7 +648,7 @@ public: }; -String *Item_nodeset_func_rootelement::val_nodeset(String *nodeset) +String *Item_nodeset_func_rootelement::val_raw(String *nodeset) { nodeset->length(0); ((XPathFilter*)nodeset)->append_element(0, 0); @@ -638,11 +656,11 @@ String *Item_nodeset_func_rootelement::val_nodeset(String *nodeset) } -String * Item_nodeset_func_union::val_nodeset(String *nodeset) +String * Item_nodeset_func_union::val_raw(String *nodeset) { uint num_nodes= pxml->length() / sizeof(MY_XML_NODE); - String set0, *s0= args[0]->val_nodeset(&set0); - String set1, *s1= args[1]->val_nodeset(&set1); + String set0, *s0= args[0]->val_raw(&set0); + String set1, *s1= args[1]->val_raw(&set1); String both_str; both_str.alloc(num_nodes); char *both= (char*) both_str.ptr(); @@ -669,7 +687,7 @@ String * Item_nodeset_func_union::val_nodeset(String *nodeset) } -String *Item_nodeset_func_selfbyname::val_nodeset(String *nodeset) +String *Item_nodeset_func_selfbyname::val_raw(String *nodeset) { prepare(nodeset); for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++) @@ -683,7 +701,7 @@ String *Item_nodeset_func_selfbyname::val_nodeset(String *nodeset) } -String *Item_nodeset_func_childbyname::val_nodeset(String *nodeset) +String *Item_nodeset_func_childbyname::val_raw(String *nodeset) { prepare(nodeset); for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++) @@ -704,7 +722,7 @@ String *Item_nodeset_func_childbyname::val_nodeset(String *nodeset) } -String *Item_nodeset_func_descendantbyname::val_nodeset(String *nodeset) +String *Item_nodeset_func_descendantbyname::val_raw(String *nodeset) { prepare(nodeset); for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++) @@ -726,7 +744,7 @@ String *Item_nodeset_func_descendantbyname::val_nodeset(String *nodeset) } -String *Item_nodeset_func_ancestorbyname::val_nodeset(String *nodeset) +String *Item_nodeset_func_ancestorbyname::val_raw(String *nodeset) { char *active; String active_str; @@ -768,7 +786,7 @@ String *Item_nodeset_func_ancestorbyname::val_nodeset(String *nodeset) } -String *Item_nodeset_func_parentbyname::val_nodeset(String *nodeset) +String *Item_nodeset_func_parentbyname::val_raw(String *nodeset) { char *active; String active_str; @@ -791,7 +809,7 @@ String *Item_nodeset_func_parentbyname::val_nodeset(String *nodeset) } -String *Item_nodeset_func_attributebyname::val_nodeset(String *nodeset) +String *Item_nodeset_func_attributebyname::val_raw(String *nodeset) { prepare(nodeset); for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++) @@ -812,7 +830,7 @@ String *Item_nodeset_func_attributebyname::val_nodeset(String *nodeset) } -String *Item_nodeset_func_predicate::val_nodeset(String *str) +String *Item_nodeset_func_predicate::val_raw(String *str) { Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0]; Item_func *comp_func= (Item_func*)args[1]; @@ -832,7 +850,7 @@ String *Item_nodeset_func_predicate::val_nodeset(String *str) } -String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset) +String *Item_nodeset_func_elementbyindex::val_raw(String *nodeset) { Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0]; prepare(nodeset); @@ -845,7 +863,9 @@ String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset) flt->pos, size); int index= (int) (args[1]->val_int()) - 1; - if (index >= 0 && (flt->pos == (uint) index || args[1]->is_bool_type())) + if (index >= 0 && + (flt->pos == (uint) index || + (args[1]->type_handler()->is_bool_type()))) ((XPathFilter*)nodeset)->append_element(flt->num, pos++); } return nodeset; @@ -858,7 +878,7 @@ String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset) */ static Item* nodeset2bool(MY_XPATH *xpath, Item *item) { - if (item->type() == Item::XPATH_NODESET) + if (item->fixed_type_handler() == &type_handler_xpath_nodeset) return new (xpath->thd->mem_root) Item_xpath_cast_bool(xpath->thd, item, xpath->pxml); return item; @@ -988,13 +1008,13 @@ static Item *create_comparator(MY_XPATH *xpath, int oper, MY_XPATH_LEX *context, Item *a, Item *b) { - if (a->type() != Item::XPATH_NODESET && - b->type() != Item::XPATH_NODESET) + if (a->fixed_type_handler() != &type_handler_xpath_nodeset && + b->fixed_type_handler() != &type_handler_xpath_nodeset) { return eq_func(xpath->thd, oper, a, b); // two scalar arguments } - else if (a->type() == Item::XPATH_NODESET && - b->type() == Item::XPATH_NODESET) + else if (a->fixed_type_handler() == &type_handler_xpath_nodeset && + b->fixed_type_handler() == &type_handler_xpath_nodeset) { uint len= (uint)(xpath->query.end - context->beg); set_if_smaller(len, 32); @@ -1019,7 +1039,7 @@ static Item *create_comparator(MY_XPATH *xpath, Item_string_xml_non_const(thd, "", 0, xpath->cs)); Item_nodeset_func *nodeset; Item *scalar, *comp; - if (a->type() == Item::XPATH_NODESET) + if (a->fixed_type_handler() == &type_handler_xpath_nodeset) { nodeset= (Item_nodeset_func*) a; scalar= b; @@ -1053,7 +1073,7 @@ static Item* nametestfunc(MY_XPATH *xpath, MEM_ROOT *mem_root= thd->mem_root; DBUG_ASSERT(arg != 0); - DBUG_ASSERT(arg->type() == Item::XPATH_NODESET); + DBUG_ASSERT(arg->fixed_type_handler() == &type_handler_xpath_nodeset); DBUG_ASSERT(beg != 0); DBUG_ASSERT(len > 0); @@ -1306,7 +1326,7 @@ static Item *create_func_substr(MY_XPATH *xpath, Item **args, uint nargs) static Item *create_func_count(MY_XPATH *xpath, Item **args, uint nargs) { - if (args[0]->type() != Item::XPATH_NODESET) + if (args[0]->fixed_type_handler() != &type_handler_xpath_nodeset) return 0; return new (xpath->thd->mem_root) Item_func_xpath_count(xpath->thd, args[0], xpath->pxml); } @@ -1314,7 +1334,7 @@ static Item *create_func_count(MY_XPATH *xpath, Item **args, uint nargs) static Item *create_func_sum(MY_XPATH *xpath, Item **args, uint nargs) { - if (args[0]->type() != Item::XPATH_NODESET) + if (args[0]->fixed_type_handler() != &type_handler_xpath_nodeset) return 0; return new (xpath->thd->mem_root) Item_func_xpath_sum(xpath->thd, args[0], xpath->pxml); @@ -1793,7 +1813,8 @@ my_xpath_parse_AxisSpecifier_NodeTest_opt_Predicate_list(MY_XPATH *xpath) xpath->item= nodeset2bool(xpath, xpath->item); - if (xpath->item->is_bool_type()) + const Type_handler *fh; + if ((fh= xpath->item->fixed_type_handler()) && fh->is_bool_type()) { xpath->context= new (xpath->thd->mem_root) Item_nodeset_func_predicate(xpath->thd, prev_context, @@ -2047,11 +2068,11 @@ static int my_xpath_parse_UnionExpr(MY_XPATH *xpath) while (my_xpath_parse_term(xpath, MY_XPATH_LEX_VLINE)) { Item *prev= xpath->item; - if (prev->type() != Item::XPATH_NODESET) + if (prev->fixed_type_handler() != &type_handler_xpath_nodeset) return 0; if (!my_xpath_parse_PathExpr(xpath) - || xpath->item->type() != Item::XPATH_NODESET) + || xpath->item->fixed_type_handler() != &type_handler_xpath_nodeset) { xpath->error= 1; return 0; @@ -2089,7 +2110,7 @@ my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(MY_XPATH *xpath) if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH)) return 1; - if (xpath->item->type() != Item::XPATH_NODESET) + if (xpath->item->fixed_type_handler() != &type_handler_xpath_nodeset) { xpath->lasttok= xpath->prevtok; xpath->error= 1; @@ -3054,7 +3075,7 @@ String *Item_func_xml_update::val_str(String *str) null_value= 0; if (!nodeset_func || get_xml(&xml) || !(rep= args[2]->val_str(&tmp_value3)) || - !(nodeset= nodeset_func->val_nodeset(&tmp_value2))) + !(nodeset= nodeset_func->val_raw(&tmp_value2))) { null_value= 1; return 0; diff --git a/sql/log.cc b/sql/log.cc index 0fa65a05de3..a56117a4ac1 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -866,10 +866,10 @@ bool Log_to_csv_event_handler:: Open_tables_backup open_tables_backup; CHARSET_INFO *client_cs= thd->variables.character_set_client; bool save_time_zone_used; - long query_time= (long) MY_MIN(query_utime/1000000, TIME_MAX_VALUE_SECONDS); - long lock_time= (long) MY_MIN(lock_utime/1000000, TIME_MAX_VALUE_SECONDS); - long query_time_micro= (long) (query_utime % 1000000); - long lock_time_micro= (long) (lock_utime % 1000000); + ulong query_time= (ulong) MY_MIN(query_utime/1000000, TIME_MAX_VALUE_SECONDS); + ulong lock_time= (ulong) MY_MIN(lock_utime/1000000, TIME_MAX_VALUE_SECONDS); + ulong query_time_micro= (ulong) (query_utime % 1000000); + ulong lock_time_micro= (ulong) (lock_utime % 1000000); DBUG_ENTER("Log_to_csv_event_handler::log_slow"); @@ -1162,6 +1162,10 @@ bool LOGGER::error_log_print(enum loglevel level, const char *format, { bool error= FALSE; Log_event_handler **current_handler; + THD *thd= current_thd; + + if (likely(thd)) + thd->error_printed_to_log= 1; /* currently we don't need locking here as there is no error_log table */ for (current_handler= error_log_handler_list ; *current_handler ;) @@ -2183,16 +2187,16 @@ void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional) { if (is_transactional) { - my_message(ER_TRANS_CACHE_FULL, ER_THD(thd, ER_TRANS_CACHE_FULL), MYF(MY_WME)); + my_message(ER_TRANS_CACHE_FULL, ER_THD(thd, ER_TRANS_CACHE_FULL), MYF(0)); } else { - my_message(ER_STMT_CACHE_FULL, ER_THD(thd, ER_STMT_CACHE_FULL), MYF(MY_WME)); + my_message(ER_STMT_CACHE_FULL, ER_THD(thd, ER_STMT_CACHE_FULL), MYF(0)); } } else { - my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno); + my_error(ER_ERROR_ON_WRITE, MYF(0), name, errno); } DBUG_VOID_RETURN; @@ -2649,7 +2653,7 @@ bool MYSQL_LOG::open( #endif if ((file= mysql_file_open(log_file_key, log_file_name, open_flags, - MYF(MY_WME | ME_WAITTANG))) < 0) + MYF(MY_WME))) < 0) goto err; if (is_fifo) @@ -2791,7 +2795,7 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name, { THD *thd= current_thd; if (unlikely(thd)) - my_error(ER_NO_UNIQUE_LOGFILE, MYF(ME_FATALERROR), log_name); + my_error(ER_NO_UNIQUE_LOGFILE, MYF(ME_FATAL), log_name); sql_print_error(ER_DEFAULT(ER_NO_UNIQUE_LOGFILE), log_name); return 1; } @@ -4627,7 +4631,7 @@ int MYSQL_BIN_LOG::open_purge_index_file(bool destroy) if (!my_b_inited(&purge_index_file)) { if ((file= my_open(purge_index_file_name, O_RDWR | O_CREAT | O_BINARY, - MYF(MY_WME | ME_WAITTANG))) < 0 || + MYF(MY_WME))) < 0 || init_io_cache(&purge_index_file, file, IO_SIZE, (destroy ? WRITE_CACHE : READ_CACHE), 0, 0, MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL))) @@ -5196,7 +5200,7 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) close_on_error= TRUE; my_printf_error(ER_ERROR_ON_WRITE, ER_THD_OR_DEFAULT(current_thd, ER_CANT_OPEN_FILE), - MYF(ME_FATALERROR), name, errno); + MYF(ME_FATAL), name, errno); goto end; } bytes_written += r.data_written; @@ -5265,7 +5269,7 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) /* handle reopening errors */ if (unlikely(error)) { - my_error(ER_CANT_OPEN_FILE, MYF(ME_FATALERROR), file_to_open, error); + my_error(ER_CANT_OPEN_FILE, MYF(ME_FATAL), file_to_open, error); close_on_error= TRUE; } @@ -7748,10 +7752,10 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry) switch (entry->error) { case ER_ERROR_ON_WRITE: - my_error(ER_ERROR_ON_WRITE, MYF(ME_NOREFRESH), name, entry->commit_errno); + my_error(ER_ERROR_ON_WRITE, MYF(ME_ERROR_LOG), name, entry->commit_errno); break; case ER_ERROR_ON_READ: - my_error(ER_ERROR_ON_READ, MYF(ME_NOREFRESH), + my_error(ER_ERROR_ON_READ, MYF(ME_ERROR_LOG), entry->error_cache->file_name, entry->commit_errno); break; default: @@ -7762,7 +7766,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry) */ my_printf_error(entry->error, "Error writing transaction to binary log: %d", - MYF(ME_NOREFRESH), entry->error); + MYF(ME_ERROR_LOG), entry->error); } /* @@ -7985,7 +7989,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) when the transaction has been safely committed in the engine. */ leader->cache_mngr->delayed_error= true; - my_error(ER_ERROR_ON_WRITE, MYF(ME_NOREFRESH), name, errno); + my_error(ER_ERROR_ON_WRITE, MYF(ME_ERROR_LOG), name, errno); check_purge= false; } /* In case of binlog rotate, update the correct current binlog offset. */ diff --git a/sql/log_event.cc b/sql/log_event.cc index 72db2717680..651fb4ce5b1 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2753,9 +2753,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, goto return_null; uint bin_size= my_decimal_get_binary_size(precision, decimals); - my_decimal dec; - binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) ptr, &dec, - precision, decimals); + my_decimal dec((const uchar *) ptr, precision, decimals); int length= DECIMAL_MAX_STR_LENGTH; char buff[DECIMAL_MAX_STR_LENGTH + 1]; decimal2string(&dec, buff, &length, 0, 0, 0); @@ -4411,7 +4409,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, size_t que have to use the transactional cache to ensure we don't calculate any checksum for the CREATE part. */ - trx_cache= (lex->select_lex.item_list.elements && + trx_cache= (lex->first_select_lex()->item_list.elements && thd->is_current_stmt_binlog_format_row()) || (thd->variables.option_bits & OPTION_GTID_BEGIN); use_cache= (lex->tmp_table() && @@ -7399,8 +7397,9 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, ex.skip_lines = skip_lines; List<Item> field_list; - thd->lex->select_lex.context.resolve_in_table_list_only(&tables); - set_fields(tables.db.str, field_list, &thd->lex->select_lex.context); + thd->lex->first_select_lex()->context.resolve_in_table_list_only(&tables); + set_fields(tables.db.str, + field_list, &thd->lex->first_select_lex()->context); thd->variables.pseudo_thread_id= thread_id; if (net) { @@ -9054,11 +9053,8 @@ void User_var_log_event::pack_info(Protocol* protocol) String buf(buf_mem, sizeof(buf_mem), system_charset_info); char buf2[DECIMAL_MAX_STR_LENGTH+1]; String str(buf2, sizeof(buf2), &my_charset_bin); - my_decimal dec; buf.length(0); - binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) (val+2), &dec, val[0], - val[1]); - my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str); + my_decimal((const uchar *) (val + 2), val[0], val[1]).to_string(&str); if (user_var_append_name_part(protocol->thd, &buf, name, name_len) || buf.append(buf2)) return; diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index 338f78d8f08..de7018c53cb 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -20,6 +20,7 @@ #ifndef MYSQL_CLIENT #include "sql_class.h" // THD +#include "field.h" #endif #define DIG_BASE 1000000000 @@ -95,9 +96,8 @@ int decimal_operation_results(int result, const char *value, const char *type) @retval E_DEC_OOM */ -int my_decimal2string(uint mask, const my_decimal *d, - uint fixed_prec, uint fixed_dec, - char filler, String *str) +int my_decimal::to_string_native(String *str, uint fixed_prec, uint fixed_dec, + char filler, uint mask) const { /* Calculate the size of the string: For DECIMAL(a,b), fixed_prec==a @@ -113,11 +113,11 @@ int my_decimal2string(uint mask, const my_decimal *d, */ int length= (fixed_prec ? (fixed_prec + ((fixed_prec == fixed_dec) ? 1 : 0) + 1) - : my_decimal_string_length(d)); + : my_decimal_string_length(this)); int result; if (str->alloc(length)) return check_result(mask, E_DEC_OOM); - result= decimal2string((decimal_t*) d, (char*) str->ptr(), + result= decimal2string(this, (char*) str->ptr(), &length, (int)fixed_prec, fixed_dec, filler); str->length(length); @@ -156,8 +156,8 @@ str_set_decimal(uint mask, const my_decimal *val, { if (!(cs->state & MY_CS_NONASCII)) { - /* For ASCII-compatible character sets we can use my_decimal2string */ - my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, str); + // For ASCII-compatible character sets we can use to_string_native() + val->to_string_native(str, fixed_prec, fixed_dec, filler, mask); str->set_charset(cs); return FALSE; } @@ -165,14 +165,13 @@ str_set_decimal(uint mask, const my_decimal *val, { /* For ASCII-incompatible character sets (like UCS2) we - call my_decimal2string() on a temporary buffer first, + call my_string_native() on a temporary buffer first, and then convert the result to the target character with help of str->copy(). */ uint errors; - char buf[DECIMAL_MAX_STR_LENGTH]; - String tmp(buf, sizeof(buf), &my_charset_latin1); - my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, &tmp); + StringBuffer<DECIMAL_MAX_STR_LENGTH> tmp; + val->to_string_native(&tmp, fixed_prec, fixed_dec, filler, mask); return str->copy(tmp.ptr(), tmp.length(), &my_charset_latin1, cs, &errors); } } @@ -182,7 +181,7 @@ str_set_decimal(uint mask, const my_decimal *val, Convert from decimal to binary representation SYNOPSIS - my_decimal2binary() + to_binary() mask error processing mask d number for conversion bin pointer to buffer where to write result @@ -199,12 +198,11 @@ str_set_decimal(uint mask, const my_decimal *val, E_DEC_OVERFLOW */ -int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec, - int scale) +int my_decimal::to_binary(uchar *bin, int prec, int scale, uint mask) const { int err1= E_DEC_OK, err2; my_decimal rounded; - my_decimal2decimal(d, &rounded); + my_decimal2decimal(this, &rounded); rounded.frac= decimal_actual_fraction(&rounded); if (scale < rounded.frac) { @@ -368,6 +366,26 @@ int my_decimal2int(uint mask, const decimal_t *d, bool unsigned_flag, } +longlong my_decimal::to_longlong(bool unsigned_flag) const +{ + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, this, unsigned_flag, &result); + return result; +} + + +my_decimal::my_decimal(Field *field) +{ + init(); + DBUG_ASSERT(!field->is_null()); +#ifndef DBUG_OFF + my_decimal *dec= +#endif + field->val_decimal(this); + DBUG_ASSERT(dec == this); +} + + #ifndef DBUG_OFF /* routines for debugging print */ diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 22800c24338..f0bb69c60c8 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -29,6 +29,8 @@ #ifndef my_decimal_h #define my_decimal_h +#include "sql_basic_types.h" + #if defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) #include "sql_string.h" /* String */ #endif @@ -39,6 +41,7 @@ C_MODE_START C_MODE_END class String; +class Field; typedef struct st_mysql_time MYSQL_TIME; /** @@ -63,6 +66,25 @@ inline int my_decimal_int_part(uint precision, uint decimals) } +#ifndef MYSQL_CLIENT +int decimal_operation_results(int result, const char *value, const char *type); +#else +inline int decimal_operation_results(int result, const char *value, + const char *type) +{ + return result; +} +#endif /*MYSQL_CLIENT*/ + + +inline int check_result(uint mask, int result) +{ + if (result & mask) + decimal_operation_results(result, "", "DECIMAL"); + return result; +} + + /** my_decimal class limits 'decimal_t' type to what we need in MySQL. @@ -125,6 +147,12 @@ public: { init(); } + my_decimal(const uchar *bin, int prec, int scale) + { + init(); + check_result(E_DEC_FATAL_ERROR, bin2decimal(bin, this, prec, scale)); + } + my_decimal(Field *field); ~my_decimal() { sanity_check(); @@ -141,7 +169,59 @@ public: bool sign() const { return decimal_t::sign; } void sign(bool s) { decimal_t::sign= s; } uint precision() const { return intg + frac; } + void set_zero() + { + /* + We need the up-cast here, since my_decimal has sign() member functions, + which conflicts with decimal_t::sign + (and decimal_make_zero is a macro, rather than a funcion). + */ + decimal_make_zero(static_cast<decimal_t*>(this)); + } + int cmp(const my_decimal *other) const + { + return decimal_cmp(this, other); + } +#ifndef MYSQL_CLIENT + bool to_bool() const + { + return !decimal_is_zero(this); + } + double to_double() const + { + double res; + decimal2double(this, &res); + return res; + } + longlong to_longlong(bool unsigned_flag) const; + // Convert to string returning decimal2string() error code + int to_string_native(String *to, uint prec, uint dec, char filler, + uint mask= E_DEC_FATAL_ERROR) const; + // Convert to string returning the String pointer + String *to_string(String *to, uint prec, uint dec, char filler) const + { + return to_string_native(to, prec, dec, filler) ? NULL : to; + } + String *to_string(String *to) const + { + return to_string(to, 0, 0, 0); + } + String *to_string_round(String *to, uint scale, my_decimal *round_buff) const + { + (void) round_to(round_buff, scale, HALF_UP); // QQ: check result? + return round_buff->to_string(to); + } + int round_to(my_decimal *to, uint scale, decimal_round_mode mode, + int mask= E_DEC_FATAL_ERROR) const + { + return check_result(mask, decimal_round(this, to, (int) scale, mode)); + } + bool to_datetime_with_warn(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate, + const char *field_name); + int to_binary(uchar *bin, int prec, int scale, + uint mask= E_DEC_FATAL_ERROR) const; +#endif /** Swap two my_decimal values */ void swap(my_decimal &rhs) { @@ -164,16 +244,6 @@ bool str_set_decimal(uint mask, const my_decimal *val, uint fixed_prec, extern my_decimal decimal_zero; -#ifndef MYSQL_CLIENT -int decimal_operation_results(int result, const char *value, const char *type); -#else -inline int decimal_operation_results(int result, const char *value, - const char *type) -{ - return result; -} -#endif /*MYSQL_CLIENT*/ - inline void max_my_decimal(my_decimal *to, int precision, int frac) { @@ -187,13 +257,6 @@ inline void max_internal_decimal(my_decimal *to) max_my_decimal(to, DECIMAL_MAX_PRECISION, 0); } -inline int check_result(uint mask, int result) -{ - if (result & mask) - decimal_operation_results(result, "", "DECIMAL"); - return result; -} - inline int check_result_and_overflow(uint mask, int result, my_decimal *val) { if (check_result(mask, result) & E_DEC_OVERFLOW) @@ -271,10 +334,6 @@ void my_decimal2decimal(const my_decimal *from, my_decimal *to) } -int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec, - int scale); - - inline int binary2my_decimal(uint mask, const uchar *bin, my_decimal *d, int prec, int scale) @@ -286,12 +345,7 @@ int binary2my_decimal(uint mask, const uchar *bin, my_decimal *d, int prec, inline int my_decimal_set_zero(my_decimal *d) { - /* - We need the up-cast here, since my_decimal has sign() member functions, - which conflicts with decimal_t::sign - (and decimal_make_zero is a macro, rather than a funcion). - */ - decimal_make_zero(static_cast<decimal_t*>(d)); + d->set_zero(); return 0; } @@ -303,40 +357,12 @@ bool my_decimal_is_zero(const my_decimal *decimal_value) } -inline -int my_decimal_round(uint mask, const my_decimal *from, int scale, - bool truncate, my_decimal *to) -{ - return check_result(mask, decimal_round(from, to, scale, - (truncate ? TRUNCATE : HALF_UP))); -} - - -inline -int my_decimal_floor(uint mask, const my_decimal *from, my_decimal *to) -{ - return check_result(mask, decimal_round(from, to, 0, FLOOR)); -} - - -inline -int my_decimal_ceiling(uint mask, const my_decimal *from, my_decimal *to) -{ - return check_result(mask, decimal_round(from, to, 0, CEILING)); -} - - inline bool str_set_decimal(const my_decimal *val, String *str, CHARSET_INFO *cs) { return str_set_decimal(E_DEC_FATAL_ERROR, val, 0, 0, 0, str, cs); } -#ifndef MYSQL_CLIENT -class String; -int my_decimal2string(uint mask, const my_decimal *d, uint fixed_prec, - uint fixed_dec, char filler, String *str); -#endif bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, ulong *microsec); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 6f0f517eade..9ff47dc1ff1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -117,6 +117,10 @@ #include <poll.h> #endif +#ifdef _WIN32 +#include <handle_connections_win.h> +#endif + #include <my_service_manager.h> #define mysqld_charset &my_charset_latin1 @@ -319,23 +323,6 @@ MY_TIMER_INFO sys_timer_info; /* static variables */ #ifdef HAVE_PSI_INTERFACE -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) -static PSI_thread_key key_thread_handle_con_namedpipes; -static PSI_cond_key key_COND_handler_count; -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#if defined(HAVE_SMEM) && !defined(EMBEDDED_LIBRARY) -static PSI_thread_key key_thread_handle_con_sharedmem; -#endif /* HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) -static PSI_thread_key key_thread_handle_con_sockets; -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#ifdef __WIN__ -static PSI_thread_key key_thread_handle_shutdown; -#endif /* __WIN__ */ - #ifdef HAVE_OPENSSL10 static PSI_rwlock_key key_rwlock_openssl; #endif @@ -371,6 +358,7 @@ static char *character_set_filesystem_name; static char *lc_messages; static char *lc_time_names_name; char *my_bind_addr_str; +int server_socket_ai_family; static char *default_collation_name; char *default_storage_engine, *default_tmp_storage_engine; char *enforced_storage_engine=NULL; @@ -744,7 +732,6 @@ mysql_mutex_t LOCK_thread_count; other threads. It also protects these variables: - handler_count in_bootstrap select_thread_in_use slave_init_thread_running @@ -1098,9 +1085,6 @@ PSI_cond_key key_COND_ack_receiver; static PSI_cond_info all_server_conds[]= { -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) - { &key_COND_handler_count, "COND_handler_count", PSI_FLAG_GLOBAL}, -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ #ifdef HAVE_MMAP { &key_PAGE_cond, "PAGE::cond", 0}, { &key_COND_active, "TC_LOG_MMAP::COND_active", 0}, @@ -1161,22 +1145,6 @@ PSI_thread_key key_thread_ack_receiver; static PSI_thread_info all_server_threads[]= { -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) - { &key_thread_handle_con_namedpipes, "con_named_pipes", PSI_FLAG_GLOBAL}, -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#if defined(HAVE_SMEM) && !defined(EMBEDDED_LIBRARY) - { &key_thread_handle_con_sharedmem, "con_shared_mem", PSI_FLAG_GLOBAL}, -#endif /* HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) - { &key_thread_handle_con_sockets, "con_sockets", PSI_FLAG_GLOBAL}, -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#ifdef __WIN__ - { &key_thread_handle_shutdown, "shutdown", PSI_FLAG_GLOBAL}, -#endif /* __WIN__ */ - { &key_thread_bootstrap, "bootstrap", PSI_FLAG_GLOBAL}, { &key_thread_delayed_insert, "delayed_insert", 0}, { &key_thread_handle_manager, "manager", PSI_FLAG_GLOBAL}, @@ -1419,10 +1387,10 @@ void Buffered_logs::print() /** Logs reported before a logger is available. */ static Buffered_logs buffered_logs; -static MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock; struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD() #ifndef EMBEDDED_LIBRARY +MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock; /** Error reporter that buffer log messages. @param level log message level @@ -1478,27 +1446,18 @@ static pthread_t select_thread; #undef getpid #include <process.h> -static mysql_cond_t COND_handler_count; -static uint handler_count; static bool start_mode=0, use_opt_args; static int opt_argc; static char **opt_argv; #if !defined(EMBEDDED_LIBRARY) -static HANDLE hEventShutdown; +HANDLE hEventShutdown; static char shutdown_event_name[40]; #include "nt_servc.h" static NTService Service; ///< Service object for WinNT #endif /* EMBEDDED_LIBRARY */ #endif /* __WIN__ */ -#ifdef _WIN32 -#include <sddl.h> /* ConvertStringSecurityDescriptorToSecurityDescriptor */ -static char pipe_name[512]; -static SECURITY_ATTRIBUTES saPipeSecurity; -static HANDLE hPipe = INVALID_HANDLE_VALUE; -#endif - #ifndef EMBEDDED_LIBRARY bool mysqld_embedded=0; #else @@ -1519,11 +1478,7 @@ int deny_severity = LOG_WARNING; ulong query_cache_min_res_unit= QUERY_CACHE_MIN_RESULT_DATA_SIZE; Query_cache query_cache; #endif -#ifdef HAVE_SMEM -const char *shared_memory_base_name= default_shared_memory_base_name; -my_bool opt_enable_shared_memory; -HANDLE smem_event_connect_request= 0; -#endif + my_bool opt_use_ssl = 0; char *opt_ssl_ca= NULL, *opt_ssl_capath= NULL, *opt_ssl_cert= NULL, @@ -1575,19 +1530,13 @@ extern "C" my_bool mysqld_get_one_option(int, const struct my_option *, char *); static int init_thread_environment(); static char *get_relative_path(const char *path); static int fix_paths(void); +#ifndef _WIN32 void handle_connections_sockets(); -#ifdef _WIN32 -pthread_handler_t handle_connections_sockets_thread(void *arg); #endif + pthread_handler_t kill_server_thread(void *arg); static void bootstrap(MYSQL_FILE *file); static bool read_init_file(char *file_name); -#ifdef _WIN32 -pthread_handler_t handle_connections_namedpipes(void *arg); -#endif -#ifdef HAVE_SMEM -pthread_handler_t handle_connections_shared_memory(void *arg); -#endif pthread_handler_t handle_slave(void *arg); static void clean_up(bool print_message); static int test_if_case_insensitive(const char *dir_name); @@ -1622,6 +1571,7 @@ static void close_connections(void) kill_cached_threads++; flush_thread_cache(); + /* kill connection thread */ #if !defined(__WIN__) DBUG_PRINT("quit", ("waiting for select thread: %lu", @@ -1671,30 +1621,7 @@ static void close_connections(void) extra_ip_sock= MYSQL_INVALID_SOCKET; } } -#ifdef _WIN32 - if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) - { - HANDLE temp; - DBUG_PRINT("quit", ("Closing named pipes") ); - - /* Create connection to the handle named pipe handler to break the loop */ - if ((temp = CreateFile(pipe_name, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL )) != INVALID_HANDLE_VALUE) - { - WaitNamedPipe(pipe_name, 1000); - DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; - SetNamedPipeHandleState(temp, &dwMode, NULL, NULL); - CancelIo(temp); - DisconnectNamedPipe(temp); - CloseHandle(temp); - } - } -#endif + #ifdef HAVE_SYS_UN_H if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET) { @@ -1941,12 +1868,6 @@ void kill_mysql(THD *thd) { DBUG_PRINT("error",("Got error: %ld from SetEvent",GetLastError())); } - /* - or: - HANDLE hEvent=OpenEvent(0, FALSE, "MySqlShutdown"); - SetEvent(hEventShutdown); - CloseHandle(hEvent); - */ } #endif #elif defined(HAVE_PTHREAD_KILL) @@ -1978,7 +1899,7 @@ void kill_mysql(THD *thd) /** Force server down. Kill all connections and threads and exit. - @param sig_ptr Signal number that caused kill_server to be called. + @param sig Signal number that caused kill_server to be called. @note A signal number of 0 mean that the function was not called @@ -1986,22 +1907,14 @@ void kill_mysql(THD *thd) or stop, we just want to kill the server. */ -#if !defined(__WIN__) -static void *kill_server(void *sig_ptr) -#define RETURN_FROM_KILL_SERVER return 0 -#else -static void __cdecl kill_server(int sig_ptr) -#define RETURN_FROM_KILL_SERVER return -#endif +static void kill_server(int sig) { DBUG_ENTER("kill_server"); #ifndef EMBEDDED_LIBRARY - int sig=(int) (long) sig_ptr; // This is passed a int // if there is a signal during the kill in progress, ignore the other if (kill_in_progress) // Safety { - DBUG_LEAVE; - RETURN_FROM_KILL_SERVER; + DBUG_VOID_RETURN; } kill_in_progress=TRUE; abort_loop=1; // This should be set @@ -2018,21 +1931,6 @@ static void __cdecl kill_server(int sig_ptr) else sql_print_error(ER_DEFAULT(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */ -#ifdef HAVE_SMEM - /* - Send event to smem_event_connect_request for aborting - */ - if (opt_enable_shared_memory) - { - if (!SetEvent(smem_event_connect_request)) - { - DBUG_PRINT("error", - ("Got error: %ld from SetEvent of smem_event_connect_request", - GetLastError())); - } - } -#endif - /* Stop wsrep threads in case they are running. */ if (wsrep_running_threads > 0) { @@ -2050,20 +1948,9 @@ static void __cdecl kill_server(int sig_ptr) else unireg_end(); - /* purecov: begin deadcode */ - DBUG_LEAVE; // Must match DBUG_ENTER() - my_thread_end(); - pthread_exit(0); - /* purecov: end */ - - RETURN_FROM_KILL_SERVER; // Avoid compiler warnings - -#else /* EMBEDDED_LIBRARY*/ - - DBUG_LEAVE; - RETURN_FROM_KILL_SERVER; +#endif /* EMBEDDED_LIBRARY*/ -#endif /* EMBEDDED_LIBRARY */ + DBUG_VOID_RETURN; } @@ -2072,11 +1959,9 @@ pthread_handler_t kill_server_thread(void *arg __attribute__((unused))) { my_thread_init(); // Initialize new thread kill_server(0); - /* purecov: begin deadcode */ my_thread_end(); pthread_exit(0); return 0; - /* purecov: end */ } #endif @@ -2122,13 +2007,7 @@ static void clean_up_error_log_mutex() void unireg_end(void) { clean_up(1); - my_thread_end(); sd_notify(0, "STATUS=MariaDB server is down"); -#if defined(SIGNALS_DONT_BREAK_READ) - exit(0); -#else - pthread_exit(0); // Exit is in main thread -#endif } @@ -2623,6 +2502,7 @@ static MYSQL_SOCKET activate_tcp_port(uint port) } else { + server_socket_ai_family= a->ai_family; sql_print_information("Server socket created on IP: '%s'.", (const char *) ip_addr); break; @@ -2749,53 +2629,16 @@ static void network_init(void) extra_ip_sock= activate_tcp_port(mysqld_extra_port); } -#ifdef _WIN32 - /* create named pipe */ - if (mysqld_unix_port[0] && !opt_bootstrap && - opt_enable_named_pipe) - { - - strxnmov(pipe_name, sizeof(pipe_name)-1, "\\\\.\\pipe\\", - mysqld_unix_port, NullS); - /* - Create a security descriptor for pipe. - - Use low integrity level, so that it is possible to connect - from any process. - - Give Everyone read/write access to pipe. - */ - if (!ConvertStringSecurityDescriptorToSecurityDescriptor( - "S:(ML;; NW;;; LW) D:(A;; FRFW;;; WD)", - SDDL_REVISION_1, &saPipeSecurity.lpSecurityDescriptor, NULL)) - { - sql_perror("Can't start server : Initialize security descriptor"); - unireg_abort(1); - } - saPipeSecurity.nLength = sizeof(SECURITY_ATTRIBUTES); - saPipeSecurity.bInheritHandle = FALSE; - if ((hPipe= CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - (int) global_system_variables.net_buffer_length, - (int) global_system_variables.net_buffer_length, - NMPWAIT_USE_DEFAULT_WAIT, - &saPipeSecurity)) == INVALID_HANDLE_VALUE) - { - sql_perror("Create named pipe failed"); - unireg_abort(1); - } - } -#endif - #if defined(HAVE_SYS_UN_H) /* ** Create the UNIX socket */ if (mysqld_unix_port[0] && !opt_bootstrap) { + size_t port_len; DBUG_PRINT("general",("UNIX Socket is %s",mysqld_unix_port)); - if (strlen(mysqld_unix_port) > (sizeof(UNIXaddr.sun_path) - 1)) + if ((port_len= strlen(mysqld_unix_port)) > sizeof(UNIXaddr.sun_path) - 1) { sql_print_error("The socket file path is too long (> %u): %s", (uint) sizeof(UNIXaddr.sun_path) - 1, mysqld_unix_port); @@ -2813,14 +2656,26 @@ static void network_init(void) bzero((char*) &UNIXaddr, sizeof(UNIXaddr)); UNIXaddr.sun_family = AF_UNIX; strmov(UNIXaddr.sun_path, mysqld_unix_port); - (void) unlink(mysqld_unix_port); +#if defined(__linux__) + /* Abstract socket */ + if (mysqld_unix_port[0] == '@') + { + UNIXaddr.sun_path[0]= '\0'; + port_len+= offsetof(struct sockaddr_un, sun_path); + } + else +#endif + { + (void) unlink(mysqld_unix_port); + port_len= sizeof(UNIXaddr); + } arg= 1; (void) mysql_socket_setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR, (char*)&arg, sizeof(arg)); umask(0); if (mysql_socket_bind(unix_sock, reinterpret_cast<struct sockaddr *>(&UNIXaddr), - sizeof(UNIXaddr)) < 0) + port_len) < 0) { sql_perror("Can't start server : Bind on unix socket"); /* purecov: tested */ sql_print_error("Do you already have another mysqld server running on socket: %s ?",mysqld_unix_port); @@ -3588,7 +3443,7 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused))) sql_print_error("Can't create thread to kill server (errno= %d)", error); #else - kill_server((void*) sig); // MIT THREAD has a alarm thread + kill_server(sig); // MIT THREAD has a alarm thread #endif } break; @@ -3650,7 +3505,7 @@ extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); void my_message_sql(uint error, const char *str, myf MyFlags) { - THD *thd= current_thd; + THD *thd= MyFlags & ME_ERROR_LOG_ONLY ? NULL : current_thd; Sql_condition::enum_warning_level level; sql_print_message_func func; DBUG_ENTER("my_message_sql"); @@ -3659,13 +3514,15 @@ void my_message_sql(uint error, const char *str, myf MyFlags) DBUG_ASSERT(str != NULL); DBUG_ASSERT(error != 0); + DBUG_ASSERT((MyFlags & ~(ME_BELL | ME_ERROR_LOG | ME_ERROR_LOG_ONLY | + ME_NOTE | ME_WARNING | ME_FATAL)) == 0); - if (MyFlags & ME_JUST_INFO) + if (MyFlags & ME_NOTE) { level= Sql_condition::WARN_LEVEL_NOTE; func= sql_print_information; } - else if (MyFlags & ME_JUST_WARNING) + else if (MyFlags & ME_WARNING) { level= Sql_condition::WARN_LEVEL_WARN; func= sql_print_warning; @@ -3678,7 +3535,7 @@ void my_message_sql(uint error, const char *str, myf MyFlags) if (likely(thd)) { - if (unlikely(MyFlags & ME_FATALERROR)) + if (unlikely(MyFlags & ME_FATAL)) thd->is_fatal_error= 1; (void) thd->raise_condition(error, NULL, level, str); } @@ -3688,7 +3545,7 @@ void my_message_sql(uint error, const char *str, myf MyFlags) /* When simulating OOM, skip writing to error log to avoid mtr errors */ DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_VOID_RETURN;); - if (unlikely(!thd) || thd->log_all_errors || (MyFlags & ME_NOREFRESH)) + if (unlikely(!thd) || thd->log_all_errors || (MyFlags & ME_ERROR_LOG)) (*func)("%s: %s", my_progname_short, str); /* purecov: inspected */ DBUG_VOID_RETURN; } @@ -3702,23 +3559,6 @@ void *my_str_malloc_mysqld(size_t size) } -#ifdef __WIN__ - -pthread_handler_t handle_shutdown(void *arg) -{ - MSG msg; - my_thread_init(); - - /* this call should create the message queue for this thread */ - PeekMessage(&msg, NULL, 1, 65534,PM_NOREMOVE); -#if !defined(EMBEDDED_LIBRARY) - if (WaitForSingleObject(hEventShutdown,INFINITE)==WAIT_OBJECT_0) -#endif /* EMBEDDED_LIBRARY */ - kill_server(MYSQL_KILL_SIGNAL); - return 0; -} -#endif - #include <mysqld_default_groups.h> #if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) @@ -5623,100 +5463,17 @@ static int init_server_components() #ifndef EMBEDDED_LIBRARY - -static void create_shutdown_thread() +#ifdef _WIN32 +static void create_shutdown_event() { -#ifdef __WIN__ hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name); - pthread_t hThread; - int error; - if (unlikely((error= mysql_thread_create(key_thread_handle_shutdown, - &hThread, &connection_attrib, - handle_shutdown, 0)))) - sql_print_warning("Can't create thread to handle shutdown requests" - " (errno= %d)", error); - // On "Stop Service" we have to do regular shutdown Service.SetShutdownEvent(hEventShutdown); -#endif /* __WIN__ */ } - -#endif /* EMBEDDED_LIBRARY */ - -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) -static void handle_connections_methods() -{ - pthread_t hThread; - int error; - DBUG_ENTER("handle_connections_methods"); - if (hPipe == INVALID_HANDLE_VALUE && - (!have_tcpip || opt_disable_networking) && - !opt_enable_shared_memory) - { - sql_print_error("TCP/IP, --shared-memory, or --named-pipe should be configured on NT OS"); - unireg_abort(1); // Will not return - } - - mysql_mutex_lock(&LOCK_start_thread); - mysql_cond_init(key_COND_handler_count, &COND_handler_count, NULL); - handler_count=0; - if (hPipe != INVALID_HANDLE_VALUE) - { - handler_count++; - if ((error= mysql_thread_create(key_thread_handle_con_namedpipes, - &hThread, &connection_attrib, - handle_connections_namedpipes, 0))) - { - sql_print_warning("Can't create thread to handle named pipes" - " (errno= %d)", error); - handler_count--; - } - } - if (have_tcpip && !opt_disable_networking) - { - handler_count++; - if ((error= mysql_thread_create(key_thread_handle_con_sockets, - &hThread, &connection_attrib, - handle_connections_sockets_thread, 0))) - { - sql_print_warning("Can't create thread to handle TCP/IP", - " (errno= %d)", error); - handler_count--; - } - } -#ifdef HAVE_SMEM - if (opt_enable_shared_memory) - { - handler_count++; - if ((error= mysql_thread_create(key_thread_handle_con_sharedmem, - &hThread, &connection_attrib, - handle_connections_shared_memory, 0))) - { - sql_print_warning("Can't create thread to handle shared memory", - " (errno= %d)", error); - handler_count--; - } - } +#else /*_WIN32*/ +#define create_shutdown_event() #endif - - while (handler_count > 0) - mysql_cond_wait(&COND_handler_count, &LOCK_start_thread); - mysql_mutex_unlock(&LOCK_start_thread); - DBUG_VOID_RETURN; -} - -void decrement_handler_count() -{ - mysql_mutex_lock(&LOCK_start_thread); - if (--handler_count == 0) - mysql_cond_signal(&COND_handler_count); - mysql_mutex_unlock(&LOCK_start_thread); - my_thread_end(); -} -#else -#define decrement_handler_count() -#endif /* defined(_WIN32) || defined(HAVE_SMEM) */ - +#endif /* EMBEDDED_LIBRARY */ #ifndef EMBEDDED_LIBRARY @@ -6119,7 +5876,7 @@ int mysqld_main(int argc, char **argv) } } - create_shutdown_thread(); + create_shutdown_event(); start_handle_manager(); /* Copy default global rpl_filter to global_rpl_filter */ @@ -6188,11 +5945,12 @@ int mysqld_main(int argc, char **argv) /* Memory used when everything is setup */ start_memory_used= global_status_var.global_memory_used; -#if defined(_WIN32) || defined(HAVE_SMEM) - handle_connections_methods(); +#ifdef _WIN32 + handle_connections_win(); + kill_server(0); #else handle_connections_sockets(); -#endif /* _WIN32 || HAVE_SMEM */ +#endif /* _WIN32 */ /* (void) pthread_attr_destroy(&connection_attrib); */ @@ -6601,7 +6359,7 @@ void create_thread_to_handle_connection(CONNECT *connect) @param[in,out] thd Thread handle of future thread. */ -static void create_new_thread(CONNECT *connect) +void create_new_thread(CONNECT *connect) { DBUG_ENTER("create_new_thread"); @@ -6669,18 +6427,107 @@ inline void kill_broken_server() #ifndef EMBEDDED_LIBRARY +void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock) +{ + CONNECT *connect; + bool is_unix_sock; + +#ifdef FD_CLOEXEC + (void) fcntl(mysql_socket_getfd(new_sock), F_SETFD, FD_CLOEXEC); +#endif + +#ifdef HAVE_LIBWRAP + { + if (mysql_socket_getfd(sock) == mysql_socket_getfd(base_ip_sock) || + mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock)) + { + struct request_info req; + signal(SIGCHLD, SIG_DFL); + request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, + mysql_socket_getfd(new_sock), NULL); + my_fromhost(&req); + if (!my_hosts_access(&req)) + { + /* + This may be stupid but refuse() includes an exit(0) + which we surely don't want... + clean_exit() - same stupid thing ... + */ + syslog(deny_severity, "refused connect from %s", + my_eval_client(&req)); + + /* + C++ sucks (the gibberish in front just translates the supplied + sink function pointer in the req structure from a void (*sink)(); + to a void(*sink)(int) if you omit the cast, the C++ compiler + will cry... + */ + if (req.sink) + ((void(*)(int))req.sink)(req.fd); + + (void)mysql_socket_shutdown(new_sock, SHUT_RDWR); + (void)mysql_socket_close(new_sock); + /* + The connection was refused by TCP wrappers. + There are no details (by client IP) available to update the + host_cache. + */ + statistic_increment(connection_errors_tcpwrap, &LOCK_status); + return; + } + } + } +#endif /* HAVE_LIBWRAP */ + + DBUG_PRINT("info", ("Creating CONNECT for new connection")); + + if ((connect= new CONNECT())) + { + is_unix_sock= (mysql_socket_getfd(sock) == + mysql_socket_getfd(unix_sock)); + + if (!(connect->vio= + mysql_socket_vio_new(new_sock, + is_unix_sock ? VIO_TYPE_SOCKET : + VIO_TYPE_TCPIP, + is_unix_sock ? VIO_LOCALHOST : 0))) + { + delete connect; + connect= 0; // Error handling below + } + } + + if (!connect) + { + /* Connect failure */ + (void)mysql_socket_close(new_sock); + statistic_increment(aborted_connects, &LOCK_status); + statistic_increment(connection_errors_internal, &LOCK_status); + return; + } + + if (is_unix_sock) + connect->host= my_localhost; + + if (mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock)) + { + connect->extra_port= 1; + connect->scheduler= extra_thread_scheduler; + } + create_new_thread(connect); +} + +#ifndef _WIN32 void handle_connections_sockets() { MYSQL_SOCKET sock= mysql_socket_invalid(); MYSQL_SOCKET new_sock= mysql_socket_invalid(); uint error_count=0; - CONNECT *connect; struct sockaddr_storage cAddr; int ip_flags __attribute__((unused))=0; int socket_flags __attribute__((unused))= 0; int extra_ip_flags __attribute__((unused))=0; int flags=0,retval; - bool is_unix_sock; #ifdef HAVE_POLL int socket_count= 0; struct pollfd fds[3]; // for ip_sock, unix_sock and extra_ip_sock @@ -6812,10 +6659,7 @@ void handle_connections_sockets() } #endif } -#if !defined(NO_FCNTL_NONBLOCK) - if (!(test_flags & TEST_BLOCKING)) - fcntl(mysql_socket_getfd(sock), F_SETFL, flags); -#endif + if (mysql_socket_getfd(new_sock) == INVALID_SOCKET) { /* @@ -6831,443 +6675,18 @@ void handle_connections_sockets() sleep(1); // Give other threads some time continue; } -#ifdef FD_CLOEXEC - (void) fcntl(mysql_socket_getfd(new_sock), F_SETFD, FD_CLOEXEC); +#if !defined(NO_FCNTL_NONBLOCK) + if (!(test_flags & TEST_BLOCKING)) + fcntl(mysql_socket_getfd(sock), F_SETFL, flags); #endif - -#ifdef HAVE_LIBWRAP - { - if (mysql_socket_getfd(sock) == mysql_socket_getfd(base_ip_sock) || - mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock)) - { - struct request_info req; - signal(SIGCHLD, SIG_DFL); - request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, - mysql_socket_getfd(new_sock), NULL); - my_fromhost(&req); - if (!my_hosts_access(&req)) - { - /* - This may be stupid but refuse() includes an exit(0) - which we surely don't want... - clean_exit() - same stupid thing ... - */ - syslog(deny_severity, "refused connect from %s", - my_eval_client(&req)); - - /* - C++ sucks (the gibberish in front just translates the supplied - sink function pointer in the req structure from a void (*sink)(); - to a void(*sink)(int) if you omit the cast, the C++ compiler - will cry... - */ - if (req.sink) - ((void (*)(int))req.sink)(req.fd); - - (void) mysql_socket_shutdown(new_sock, SHUT_RDWR); - (void) mysql_socket_close(new_sock); - /* - The connection was refused by TCP wrappers. - There are no details (by client IP) available to update the - host_cache. - */ - statistic_increment(connection_errors_tcpwrap, &LOCK_status); - continue; - } - } - } -#endif /* HAVE_LIBWRAP */ - - DBUG_PRINT("info", ("Creating CONNECT for new connection")); - - if ((connect= new CONNECT())) - { - is_unix_sock= (mysql_socket_getfd(sock) == - mysql_socket_getfd(unix_sock)); - - if (!(connect->vio= - mysql_socket_vio_new(new_sock, - is_unix_sock ? VIO_TYPE_SOCKET : - VIO_TYPE_TCPIP, - is_unix_sock ? VIO_LOCALHOST: 0))) - { - delete connect; - connect= 0; // Error handling below - } - } - - if (!connect) - { - /* Connect failure */ - (void) mysql_socket_shutdown(new_sock, SHUT_RDWR); - (void) mysql_socket_close(new_sock); - statistic_increment(aborted_connects,&LOCK_status); - statistic_increment(connection_errors_internal, &LOCK_status); - continue; - } - - if (is_unix_sock) - connect->host= my_localhost; - - if (mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock)) - { - connect->extra_port= 1; - connect->scheduler= extra_thread_scheduler; - } - create_new_thread(connect); + handle_accepted_socket(new_sock, sock); } sd_notify(0, "STOPPING=1\n" "STATUS=Shutdown in progress\n"); DBUG_VOID_RETURN; } - -#ifdef _WIN32 -pthread_handler_t handle_connections_sockets_thread(void *arg) -{ - my_thread_init(); - handle_connections_sockets(); - decrement_handler_count(); - return 0; -} - -pthread_handler_t handle_connections_namedpipes(void *arg) -{ - HANDLE hConnectedPipe; - OVERLAPPED connectOverlapped= {0}; - my_thread_init(); - DBUG_ENTER("handle_connections_namedpipes"); - connectOverlapped.hEvent= CreateEvent(NULL, TRUE, FALSE, NULL); - if (!connectOverlapped.hEvent) - { - sql_print_error("Can't create event, last error=%u", GetLastError()); - unireg_abort(1); - } - DBUG_PRINT("general",("Waiting for named pipe connections.")); - while (!abort_loop) - { - /* wait for named pipe connection */ - BOOL fConnected= ConnectNamedPipe(hPipe, &connectOverlapped); - if (!fConnected && (GetLastError() == ERROR_IO_PENDING)) - { - /* - ERROR_IO_PENDING says async IO has started but not yet finished. - GetOverlappedResult will wait for completion. - */ - DWORD bytes; - fConnected= GetOverlappedResult(hPipe, &connectOverlapped,&bytes, TRUE); - } - if (abort_loop) - break; - if (!fConnected) - fConnected = GetLastError() == ERROR_PIPE_CONNECTED; - if (!fConnected) - { - CloseHandle(hPipe); - if ((hPipe= CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX | - FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | - PIPE_READMODE_BYTE | - PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - (int) global_system_variables. - net_buffer_length, - (int) global_system_variables. - net_buffer_length, - NMPWAIT_USE_DEFAULT_WAIT, - &saPipeSecurity)) == - INVALID_HANDLE_VALUE) - { - sql_perror("Can't create new named pipe!"); - break; // Abort - } - } - hConnectedPipe = hPipe; - /* create new pipe for new connection */ - if ((hPipe = CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX | - FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | - PIPE_READMODE_BYTE | - PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - (int) global_system_variables.net_buffer_length, - (int) global_system_variables.net_buffer_length, - NMPWAIT_USE_DEFAULT_WAIT, - &saPipeSecurity)) == - INVALID_HANDLE_VALUE) - { - sql_perror("Can't create new named pipe!"); - hPipe=hConnectedPipe; - continue; // We have to try again - } - CONNECT *connect; - if (!(connect= new CONNECT) || - !(connect->vio= vio_new_win32pipe(hConnectedPipe))) - { - DisconnectNamedPipe(hConnectedPipe); - CloseHandle(hConnectedPipe); - delete connect; - statistic_increment(aborted_connects,&LOCK_status); - statistic_increment(connection_errors_internal, &LOCK_status); - continue; - } - connect->host= my_localhost; - create_new_thread(connect); - } - LocalFree(saPipeSecurity.lpSecurityDescriptor); - CloseHandle(connectOverlapped.hEvent); - DBUG_LEAVE; - decrement_handler_count(); - return 0; -} -#endif /* _WIN32 */ - - -#ifdef HAVE_SMEM - -/** - Thread of shared memory's service. - - @param arg Arguments of thread -*/ -pthread_handler_t handle_connections_shared_memory(void *arg) -{ - /* file-mapping object, use for create shared memory */ - HANDLE handle_connect_file_map= 0; - char *handle_connect_map= 0; // pointer on shared memory - HANDLE event_connect_answer= 0; - ulong smem_buffer_length= shared_memory_buffer_length + 4; - ulong connect_number= 1; - char *tmp= NULL; - char *suffix_pos; - char connect_number_char[22], *p; - const char *errmsg= 0; - SECURITY_ATTRIBUTES *sa_event= 0, *sa_mapping= 0; - my_thread_init(); - DBUG_ENTER("handle_connections_shared_memorys"); - DBUG_PRINT("general",("Waiting for allocated shared memory.")); - - /* - get enough space base-name + '_' + longest suffix we might ever send - */ - if (!(tmp= (char *)my_malloc(strlen(shared_memory_base_name) + 32L, - MYF(MY_FAE)))) - goto error; - - if (my_security_attr_create(&sa_event, &errmsg, - GENERIC_ALL, SYNCHRONIZE | EVENT_MODIFY_STATE)) - goto error; - - if (my_security_attr_create(&sa_mapping, &errmsg, - GENERIC_ALL, FILE_MAP_READ | FILE_MAP_WRITE)) - goto error; - - /* - The name of event and file-mapping events create agree next rule: - shared_memory_base_name+unique_part - Where: - shared_memory_base_name is unique value for each server - unique_part is unique value for each object (events and file-mapping) - */ - suffix_pos= strxmov(tmp,shared_memory_base_name,"_",NullS); - strmov(suffix_pos, "CONNECT_REQUEST"); - if ((smem_event_connect_request= CreateEvent(sa_event, - FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create request event"; - goto error; - } - strmov(suffix_pos, "CONNECT_ANSWER"); - if ((event_connect_answer= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0) - { - errmsg="Could not create answer event"; - goto error; - } - strmov(suffix_pos, "CONNECT_DATA"); - if ((handle_connect_file_map= - CreateFileMapping(INVALID_HANDLE_VALUE, sa_mapping, - PAGE_READWRITE, 0, sizeof(connect_number), tmp)) == 0) - { - errmsg= "Could not create file mapping"; - goto error; - } - if ((handle_connect_map= (char *)MapViewOfFile(handle_connect_file_map, - FILE_MAP_WRITE,0,0, - sizeof(DWORD))) == 0) - { - errmsg= "Could not create shared memory service"; - goto error; - } - - while (!abort_loop) - { - /* Wait a request from client */ - WaitForSingleObject(smem_event_connect_request,INFINITE); - - /* - it can be after shutdown command - */ - if (abort_loop) - goto error; - - HANDLE handle_client_file_map= 0; - char *handle_client_map= 0; - HANDLE event_client_wrote= 0; - HANDLE event_client_read= 0; // for transfer data server <-> client - HANDLE event_server_wrote= 0; - HANDLE event_server_read= 0; - HANDLE event_conn_closed= 0; - CONNECT *connect= 0; - - p= int10_to_str(connect_number, connect_number_char, 10); - /* - The name of event and file-mapping events create agree next rule: - shared_memory_base_name+unique_part+number_of_connection - Where: - shared_memory_base_name is uniquel value for each server - unique_part is unique value for each object (events and file-mapping) - number_of_connection is connection-number between server and client - */ - suffix_pos= strxmov(tmp,shared_memory_base_name,"_",connect_number_char, - "_",NullS); - strmov(suffix_pos, "DATA"); - if ((handle_client_file_map= - CreateFileMapping(INVALID_HANDLE_VALUE, sa_mapping, - PAGE_READWRITE, 0, smem_buffer_length, tmp)) == 0) - { - errmsg= "Could not create file mapping"; - goto errorconn; - } - if ((handle_client_map= (char*)MapViewOfFile(handle_client_file_map, - FILE_MAP_WRITE,0,0, - smem_buffer_length)) == 0) - { - errmsg= "Could not create memory map"; - goto errorconn; - } - strmov(suffix_pos, "CLIENT_WROTE"); - if ((event_client_wrote= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create client write event"; - goto errorconn; - } - strmov(suffix_pos, "CLIENT_READ"); - if ((event_client_read= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create client read event"; - goto errorconn; - } - strmov(suffix_pos, "SERVER_READ"); - if ((event_server_read= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create server read event"; - goto errorconn; - } - strmov(suffix_pos, "SERVER_WROTE"); - if ((event_server_wrote= CreateEvent(sa_event, - FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create server write event"; - goto errorconn; - } - strmov(suffix_pos, "CONNECTION_CLOSED"); - if ((event_conn_closed= CreateEvent(sa_event, - TRUE, FALSE, tmp)) == 0) - { - errmsg= "Could not create closed connection event"; - goto errorconn; - } - if (abort_loop) - goto errorconn; - - if (!(connect= new CONNECT)) - { - errmsg= "Could not create CONNECT object"; - goto errorconn; - } - - /* Send number of connection to client */ - int4store(handle_connect_map, connect_number); - if (!SetEvent(event_connect_answer)) - { - errmsg= "Could not send answer event"; - goto errorconn; - } - /* Set event that client should receive data */ - if (!SetEvent(event_client_read)) - { - errmsg= "Could not set client to read mode"; - goto errorconn; - } - if (!(connect->vio= vio_new_win32shared_memory(handle_client_file_map, - handle_client_map, - event_client_wrote, - event_client_read, - event_server_wrote, - event_server_read, - event_conn_closed))) - { - errmsg= "Could not create VIO object"; - goto errorconn; - } - connect->host= my_localhost; /* Host is unknown */ - create_new_thread(connect); - connect_number++; - continue; - -errorconn: - /* Could not form connection; Free used handlers/memort and retry */ - if (errmsg) - { - char buff[180]; - strxmov(buff, "Can't create shared memory connection: ", errmsg, ".", - NullS); - sql_perror(buff); - } - if (handle_client_file_map) - CloseHandle(handle_client_file_map); - if (handle_client_map) - UnmapViewOfFile(handle_client_map); - if (event_server_wrote) - CloseHandle(event_server_wrote); - if (event_server_read) - CloseHandle(event_server_read); - if (event_client_wrote) - CloseHandle(event_client_wrote); - if (event_client_read) - CloseHandle(event_client_read); - if (event_conn_closed) - CloseHandle(event_conn_closed); - - delete connect; - statistic_increment(aborted_connects,&LOCK_status); - statistic_increment(connection_errors_internal, &LOCK_status); - } - - /* End shared memory handling */ -error: - if (tmp) - my_free(tmp); - - if (errmsg) - { - char buff[180]; - strxmov(buff, "Can't create shared memory service: ", errmsg, ".", NullS); - sql_perror(buff); - } - my_security_attr_free(sa_event); - my_security_attr_free(sa_mapping); - if (handle_connect_map) UnmapViewOfFile(handle_connect_map); - if (handle_connect_file_map) CloseHandle(handle_connect_file_map); - if (event_connect_answer) CloseHandle(event_connect_answer); - if (smem_event_connect_request) CloseHandle(smem_event_connect_request); - DBUG_LEAVE; - decrement_handler_count(); - return 0; -} -#endif /* HAVE_SMEM */ +#endif /* _WIN32*/ #endif /* EMBEDDED_LIBRARY */ @@ -8957,7 +8376,9 @@ static int mysql_init_variables(void) character_set_filesystem= &my_charset_bin; opt_specialflag= SPECIAL_ENGLISH; +#ifndef EMBEDDED_LIBRARY unix_sock= base_ip_sock= extra_ip_sock= MYSQL_INVALID_SOCKET; +#endif mysql_home_ptr= mysql_home; log_error_file_ptr= log_error_file; protocol_version= PROTOCOL_VERSION; @@ -9071,9 +8492,6 @@ static int mysql_init_variables(void) ssl_acceptor_fd= 0; #endif /* ! EMBEDDED_LIBRARY */ #endif /* HAVE_OPENSSL */ -#ifdef HAVE_SMEM - shared_memory_base_name= default_shared_memory_base_name; -#endif #if defined(__WIN__) /* Allow Win32 users to move MySQL anywhere */ @@ -9816,10 +9234,10 @@ static int get_options(int *argc_ptr, char ***argv_ptr) errors. */ if (global_system_variables.log_warnings >= 10) - my_global_flags= MY_WME | ME_JUST_INFO; + my_global_flags= MY_WME | ME_NOTE; /* Log all errors not handled by thd->handle_error() to my_message_sql() */ if (global_system_variables.log_warnings >= 11) - my_global_flags|= ME_NOREFRESH; + my_global_flags|= ME_ERROR_LOG; if (my_assert_on_error) debug_assert_if_crashed_table= 1; diff --git a/sql/mysqld.h b/sql/mysqld.h index d5cabd790b2..75f35a6df24 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -24,6 +24,7 @@ #include "mysql_com.h" /* SERVER_VERSION_LENGTH */ #include "my_atomic.h" #include "mysql/psi/mysql_file.h" /* MYSQL_FILE */ +#include "mysql/psi/mysql_socket.h" /* MYSQL_SOCKET */ #include "sql_list.h" /* I_List */ #include "sql_cmd.h" #include <my_rnd.h> @@ -92,6 +93,8 @@ void refresh_status(THD *thd); bool is_secure_file_path(char *path); void dec_connection_count(scheduler_functions *scheduler); extern void init_net_server_extension(THD *thd); +extern void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock); +extern void create_new_thread(CONNECT *connect); extern "C" MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info; extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *files_charset_info ; @@ -152,6 +155,7 @@ extern ulong opt_replicate_events_marked_for_skip; extern char *default_tz_name; extern Time_zone *default_tz; extern char *my_bind_addr_str; +extern int server_socket_ai_family; extern char *default_storage_engine, *default_tmp_storage_engine; extern char *enforced_storage_engine; extern char *gtid_pos_auto_engines; @@ -760,7 +764,7 @@ enum enum_query_type /* query_id */ extern query_id_t global_query_id; -ATTRIBUTE_NORETURN void unireg_end(void); +void unireg_end(void); /* increment query_id and return it. */ inline __attribute__((warn_unused_result)) query_id_t next_query_id() diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 94cfae2664a..d6db365a8a2 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1479,7 +1479,6 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler, { handler *save_file= file, *org_file; THD *thd= head->in_use; - MY_BITMAP * const save_vcol_set= head->vcol_set; MY_BITMAP * const save_read_set= head->read_set; MY_BITMAP * const save_write_set= head->write_set; DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan"); @@ -1537,14 +1536,14 @@ end: org_file= head->file; head->file= file; - head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap, &column_bitmap); + head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap); head->prepare_for_keyread(index, &column_bitmap); head->prepare_for_position(); head->file= org_file; /* Restore head->read_set (and write_set) to what they had before the call */ - head->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set); + head->column_bitmaps_set(save_read_set, save_write_set); if (reset()) { @@ -1559,7 +1558,7 @@ end: DBUG_RETURN(0); failure: - head->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set); + head->column_bitmaps_set(save_read_set, save_write_set); delete file; file= save_file; DBUG_RETURN(1); @@ -1893,6 +1892,118 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_, left=right= &null_element; } + +/* + A number of helper classes: + SEL_ARG_LE, SEL_ARG_LT, SEL_ARG_GT, SEL_ARG_GE, + to share the code between: + Field::stored_field_make_mm_leaf() + Field::stored_field_make_mm_leaf_exact() +*/ +class SEL_ARG_LE: public SEL_ARG +{ +public: + SEL_ARG_LE(const uchar *key, Field *field) + :SEL_ARG(field, key, key) + { + if (!field->real_maybe_null()) + min_flag= NO_MIN_RANGE; // From start + else + { + min_value= is_null_string; + min_flag= NEAR_MIN; // > NULL + } + } +}; + + +class SEL_ARG_LT: public SEL_ARG_LE +{ +public: + /* + Use this constructor if value->save_in_field() went precisely, + without any data rounding or truncation. + */ + SEL_ARG_LT(const uchar *key, Field *field) + :SEL_ARG_LE(key, field) + { max_flag= NEAR_MAX; } + /* + Use this constructor if value->save_in_field() returned success, + but we don't know if rounding or truncation happened + (as some Field::store() do not report minor data changes). + */ + SEL_ARG_LT(THD *thd, const uchar *key, Field *field, Item *value) + :SEL_ARG_LE(key, field) + { + if (stored_field_cmp_to_item(thd, field, value) == 0) + max_flag= NEAR_MAX; + } +}; + + +class SEL_ARG_GT: public SEL_ARG +{ +public: + /* + Use this constructor if value->save_in_field() went precisely, + without any data rounding or truncation. + */ + SEL_ARG_GT(const uchar *key, const KEY_PART *key_part, Field *field) + :SEL_ARG(field, key, key) + { + // Don't use open ranges for partial key_segments + if (!(key_part->flag & HA_PART_KEY_SEG)) + min_flag= NEAR_MIN; + max_flag= NO_MAX_RANGE; + } + /* + Use this constructor if value->save_in_field() returned success, + but we don't know if rounding or truncation happened + (as some Field::store() do not report minor data changes). + */ + SEL_ARG_GT(THD *thd, const uchar *key, + const KEY_PART *key_part, Field *field, Item *value) + :SEL_ARG(field, key, key) + { + // Don't use open ranges for partial key_segments + if ((!(key_part->flag & HA_PART_KEY_SEG)) && + (stored_field_cmp_to_item(thd, field, value) <= 0)) + min_flag= NEAR_MIN; + max_flag= NO_MAX_RANGE; + } +}; + + +class SEL_ARG_GE: public SEL_ARG +{ +public: + /* + Use this constructor if value->save_in_field() went precisely, + without any data rounding or truncation. + */ + SEL_ARG_GE(const uchar *key, Field *field) + :SEL_ARG(field, key, key) + { + max_flag= NO_MAX_RANGE; + } + /* + Use this constructor if value->save_in_field() returned success, + but we don't know if rounding or truncation happened + (as some Field::store() do not report minor data changes). + */ + SEL_ARG_GE(THD *thd, const uchar *key, + const KEY_PART *key_part, Field *field, Item *value) + :SEL_ARG(field, key, key) + { + // Don't use open ranges for partial key_segments + if ((!(key_part->flag & HA_PART_KEY_SEG)) && + (stored_field_cmp_to_item(thd, field, value) < 0)) + min_flag= NEAR_MIN; + max_flag= NO_MAX_RANGE; + } +}; + + SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, SEL_ARG **next_arg) { @@ -4571,7 +4682,7 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) if (max_cost != DBL_MAX && (busy_blocks+index_reads_cost) >= n_blocks) return 1; */ - JOIN *join= param->thd->lex->select_lex.join; + JOIN *join= param->thd->lex->first_select_lex()->join; if (!join || join->table_count == 1) { /* No join, assume reading is done in one 'sweep' */ @@ -8042,52 +8153,112 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param, SEL_ARG * Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param, Field *field, KEY_PART *key_part, - Item_func::Functype type, Item *value) + Item_func::Functype functype, Item *value) { - uint maybe_null=(uint) field->real_maybe_null(); - SEL_ARG *tree= 0; - MEM_ROOT *alloc= param->mem_root; - uchar *str; - int err; DBUG_ENTER("Item_bool_func::get_mm_leaf"); - DBUG_ASSERT(value); // IS NULL and IS NOT NULL are handled separately - if (key_part->image_type != Field::itRAW) DBUG_RETURN(0); // e.g. SPATIAL index + DBUG_RETURN(field->get_mm_leaf(param, key_part, this, + functype_to_scalar_comparison_op(functype), + value)); +} - if (param->using_real_indexes && - !field->optimize_range(param->real_keynr[key_part->key], - key_part->part) && - type != EQ_FUNC && - type != EQUAL_FUNC) - goto end; // Can't optimize this - if (!field->can_optimize_range(this, value, - type == EQUAL_FUNC || type == EQ_FUNC)) - goto end; +bool Field::can_optimize_scalar_range(const RANGE_OPT_PARAM *param, + const KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, + const Item *value) const +{ + bool is_eq_func= op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL; + if ((param->using_real_indexes && + !optimize_range(param->real_keynr[key_part->key], + key_part->part) && !is_eq_func) || + !can_optimize_range(cond, value, is_eq_func)) + return false; + return true; +} + + +uchar *Field::make_key_image(MEM_ROOT *mem_root, const KEY_PART *key_part) +{ + DBUG_ENTER("Field::make_key_image"); + uint maybe_null= (uint) real_maybe_null(); + uchar *str; + if (!(str= (uchar*) alloc_root(mem_root, key_part->store_length + 1))) + DBUG_RETURN(0); + if (maybe_null) + *str= (uchar) is_real_null(); // Set to 1 if null + get_key_image(str + maybe_null, key_part->length, key_part->image_type); + DBUG_RETURN(str); +} + + +SEL_ARG *Field::stored_field_make_mm_leaf_truncated(RANGE_OPT_PARAM *param, + scalar_comparison_op op, + Item *value) +{ + DBUG_ENTER("Field::stored_field_make_mm_leaf_truncated"); + if ((op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) && + value->result_type() == item_cmp_type(result_type(), + value->result_type())) + DBUG_RETURN(new (param->mem_root) SEL_ARG_IMPOSSIBLE(this)); + /* + TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE + for the cases like int_field > 999999999999999999999999 as well. + */ + DBUG_RETURN(0); +} + - err= value->save_in_field_no_warnings(field, 1); +SEL_ARG *Field_num::get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value) +{ + DBUG_ENTER("Field_num::get_mm_leaf"); + if (!can_optimize_scalar_range(prm, key_part, cond, op, value)) + DBUG_RETURN(0); + int err= value->save_in_field_no_warnings(this, 1); + if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0) + DBUG_RETURN(&null_element); + if (err > 0 && cmp_type() != value->result_type()) + DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value)); + DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value)); +} + + +SEL_ARG *Field_temporal::get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value) +{ + DBUG_ENTER("Field_temporal::get_mm_leaf"); + if (!can_optimize_scalar_range(prm, key_part, cond, op, value)) + DBUG_RETURN(0); + int err= value->save_in_field_no_warnings(this, 1); + if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0) + DBUG_RETURN(&null_element); if (err > 0) - { - if (field->type_handler() == &type_handler_enum || - field->type_handler() == &type_handler_set) - { - if (type == EQ_FUNC || type == EQUAL_FUNC) - tree= new (alloc) SEL_ARG_IMPOSSIBLE(field); - goto end; - } + DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value)); + DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value)); +} - if (err == 2 && field->cmp_type() == STRING_RESULT) - { - if (type == EQ_FUNC || type == EQUAL_FUNC) - tree= new (alloc) SEL_ARG_IMPOSSIBLE(field); - else - tree= NULL; /* Cannot infer anything */ - goto end; - } - if (err == 3 && field->type() == FIELD_TYPE_DATE) +SEL_ARG *Field_date_common::get_mm_leaf(RANGE_OPT_PARAM *prm, + KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, + Item *value) +{ + DBUG_ENTER("Field_date_common::get_mm_leaf"); + if (!can_optimize_scalar_range(prm, key_part, cond, op, value)) + DBUG_RETURN(0); + int err= value->save_in_field_no_warnings(this, 1); + if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0) + DBUG_RETURN(&null_element); + if (err > 0) + { + if (err == 3) { /* We were saving DATETIME into a DATE column, the conversion went ok @@ -8107,76 +8278,86 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param, be done together with other types at the end of this function (grep for stored_field_cmp_to_item) */ - if (type == EQ_FUNC || type == EQUAL_FUNC) - { - tree= new (alloc) SEL_ARG_IMPOSSIBLE(field); - goto end; - } - // Continue with processing non-equality ranges - } - else if (field->cmp_type() != value->result_type()) - { - if ((type == EQ_FUNC || type == EQUAL_FUNC) && - value->result_type() == item_cmp_type(field->result_type(), - value->result_type())) - { - tree= new (alloc) SEL_ARG_IMPOSSIBLE(field); - goto end; - } - else - { - /* - TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE - for the cases like int_field > 999999999999999999999999 as well. - */ - tree= 0; - goto end; - } - } - - /* - guaranteed at this point: err > 0; field and const of same type - If an integer got bounded (e.g. to within 0..255 / -128..127) - for < or >, set flags as for <= or >= (no NEAR_MAX / NEAR_MIN) - */ - else if (err == 1 && field->result_type() == INT_RESULT) - { - if (type == LT_FUNC && (value->val_int() > 0)) - type= LE_FUNC; - else if (type == GT_FUNC && - (field->type() != FIELD_TYPE_BIT) && - !((Field_num*)field)->unsigned_flag && - !((Item_int*)value)->unsigned_flag && - (value->val_int() < 0)) - type= GE_FUNC; + if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) + DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this)); + DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value)); } + DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value)); } - else if (err < 0) + DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value)); +} + + +SEL_ARG *Field_str::get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value) +{ + DBUG_ENTER("Field_str::get_mm_leaf"); + if (!can_optimize_scalar_range(prm, key_part, cond, op, value)) + DBUG_RETURN(0); + int err= value->save_in_field_no_warnings(this, 1); + if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0) + DBUG_RETURN(&null_element); + if (err > 0) { - /* This happens when we try to insert a NULL field in a not null column */ - tree= &null_element; // cmp with NULL is never TRUE - goto end; + if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) + DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this)); + DBUG_RETURN(NULL); /* Cannot infer anything */ } + DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value)); +} - /* - Any sargable predicate except "<=>" involving NULL as a constant is always - FALSE - */ - if (type != EQUAL_FUNC && field->is_real_null()) + +SEL_ARG *Field::get_mm_leaf_int(RANGE_OPT_PARAM *prm, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value, + bool unsigned_field) +{ + DBUG_ENTER("Field::get_mm_leaf_int"); + if (!can_optimize_scalar_range(prm, key_part, cond, op, value)) + DBUG_RETURN(0); + int err= value->save_in_field_no_warnings(this, 1); + if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0) + DBUG_RETURN(&null_element); + if (err > 0) { - tree= &null_element; - goto end; + if (value->result_type() != INT_RESULT) + DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value)); + else + DBUG_RETURN(stored_field_make_mm_leaf_bounded_int(prm, key_part, + op, value, + unsigned_field)); } - - str= (uchar*) alloc_root(alloc, key_part->store_length+1); - if (!str) - goto end; - if (maybe_null) - *str= (uchar) field->is_real_null(); // Set to 1 if null - field->get_key_image(str+maybe_null, key_part->length, - key_part->image_type); - if (!(tree= new (alloc) SEL_ARG(field, str, str))) - goto end; // out of memory + if (value->result_type() != INT_RESULT) + DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value)); + DBUG_RETURN(stored_field_make_mm_leaf_exact(prm, key_part, op, value)); +} + + +/* + This method is called when: + - value->save_in_field_no_warnings() returned err > 0 + - and both field and "value" are of integer data types + If an integer got bounded (e.g. to within 0..255 / -128..127) + for < or >, set flags as for <= or >= (no NEAR_MAX / NEAR_MIN) +*/ + +SEL_ARG *Field::stored_field_make_mm_leaf_bounded_int(RANGE_OPT_PARAM *param, + KEY_PART *key_part, + scalar_comparison_op op, + Item *value, + bool unsigned_field) +{ + DBUG_ENTER("Field::stored_field_make_mm_leaf_bounded_int"); + if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) // e.g. tinyint = 200 + DBUG_RETURN(new (param->mem_root) SEL_ARG_IMPOSSIBLE(this)); + longlong item_val= value->val_int(); + + if (op == SCALAR_CMP_LT && item_val > 0) + op= SCALAR_CMP_LE; // e.g. rewrite (tinyint < 200) to (tinyint <= 127) + else if (op == SCALAR_CMP_GT && !unsigned_field && + !value->unsigned_flag && item_val < 0) + op= SCALAR_CMP_GE; // e.g. rewrite (tinyint > -200) to (tinyint >= -128) /* Check if we are comparing an UNSIGNED integer with a negative constant. @@ -8189,66 +8370,74 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param, negative integers (which otherwise fails because at query execution time negative integers are cast to unsigned if compared with unsigned). */ - if (field->result_type() == INT_RESULT && - value->result_type() == INT_RESULT && - ((field->type() == FIELD_TYPE_BIT || - ((Field_num *) field)->unsigned_flag) && - !((Item_int*) value)->unsigned_flag)) + if (unsigned_field && !value->unsigned_flag && item_val < 0) { - longlong item_val= value->val_int(); - if (item_val < 0) - { - if (type == LT_FUNC || type == LE_FUNC) - { - tree->type= SEL_ARG::IMPOSSIBLE; - goto end; - } - if (type == GT_FUNC || type == GE_FUNC) - { - tree= 0; - goto end; - } - } + if (op == SCALAR_CMP_LT || op == SCALAR_CMP_LE) // e.g. uint < -1 + DBUG_RETURN(new (param->mem_root) SEL_ARG_IMPOSSIBLE(this)); + if (op == SCALAR_CMP_GT || op == SCALAR_CMP_GE) // e.g. uint > -1 + DBUG_RETURN(0); } + DBUG_RETURN(stored_field_make_mm_leaf_exact(param, key_part, op, value)); +} - switch (type) { - case LT_FUNC: - if (stored_field_cmp_to_item(param->thd, field, value) == 0) - tree->max_flag=NEAR_MAX; - /* fall through */ - case LE_FUNC: - if (!maybe_null) - tree->min_flag=NO_MIN_RANGE; /* From start */ - else - { // > NULL - tree->min_value=is_null_string; - tree->min_flag=NEAR_MIN; - } - break; - case GT_FUNC: - /* Don't use open ranges for partial key_segments */ - if ((!(key_part->flag & HA_PART_KEY_SEG)) && - (stored_field_cmp_to_item(param->thd, field, value) <= 0)) - tree->min_flag=NEAR_MIN; - tree->max_flag= NO_MAX_RANGE; - break; - case GE_FUNC: - /* Don't use open ranges for partial key_segments */ - if ((!(key_part->flag & HA_PART_KEY_SEG)) && - (stored_field_cmp_to_item(param->thd, field, value) < 0)) - tree->min_flag= NEAR_MIN; - tree->max_flag=NO_MAX_RANGE; - break; - case EQ_FUNC: - case EQUAL_FUNC: - break; - default: - DBUG_ASSERT(0); + +SEL_ARG *Field::stored_field_make_mm_leaf(RANGE_OPT_PARAM *param, + KEY_PART *key_part, + scalar_comparison_op op, + Item *value) +{ + DBUG_ENTER("Field::stored_field_make_mm_leaf"); + THD *thd= param->thd; + MEM_ROOT *mem_root= param->mem_root; + uchar *str; + if (!(str= make_key_image(param->mem_root, key_part))) + DBUG_RETURN(0); + + switch (op) { + case SCALAR_CMP_LE: + DBUG_RETURN(new (mem_root) SEL_ARG_LE(str, this)); + case SCALAR_CMP_LT: + DBUG_RETURN(new (mem_root) SEL_ARG_LT(thd, str, this, value)); + case SCALAR_CMP_GT: + DBUG_RETURN(new (mem_root) SEL_ARG_GT(thd, str, key_part, this, value)); + case SCALAR_CMP_GE: + DBUG_RETURN(new (mem_root) SEL_ARG_GE(thd, str, key_part, this, value)); + case SCALAR_CMP_EQ: + case SCALAR_CMP_EQUAL: + DBUG_RETURN(new (mem_root) SEL_ARG(this, str, str)); break; } + DBUG_ASSERT(0); + DBUG_RETURN(NULL); +} -end: - DBUG_RETURN(tree); + +SEL_ARG *Field::stored_field_make_mm_leaf_exact(RANGE_OPT_PARAM *param, + KEY_PART *key_part, + scalar_comparison_op op, + Item *value) +{ + DBUG_ENTER("Field::stored_field_make_mm_leaf_exact"); + uchar *str; + if (!(str= make_key_image(param->mem_root, key_part))) + DBUG_RETURN(0); + + switch (op) { + case SCALAR_CMP_LE: + DBUG_RETURN(new (param->mem_root) SEL_ARG_LE(str, this)); + case SCALAR_CMP_LT: + DBUG_RETURN(new (param->mem_root) SEL_ARG_LT(str, this)); + case SCALAR_CMP_GT: + DBUG_RETURN(new (param->mem_root) SEL_ARG_GT(str, key_part, this)); + case SCALAR_CMP_GE: + DBUG_RETURN(new (param->mem_root) SEL_ARG_GE(str, this)); + case SCALAR_CMP_EQ: + case SCALAR_CMP_EQUAL: + DBUG_RETURN(new (param->mem_root) SEL_ARG(this, str, str)); + break; + } + DBUG_ASSERT(0); + DBUG_RETURN(NULL); } @@ -11378,7 +11567,6 @@ int QUICK_RANGE_SELECT::reset() HANDLER_BUFFER empty_buf; MY_BITMAP * const save_read_set= head->read_set; MY_BITMAP * const save_write_set= head->write_set; - MY_BITMAP * const save_vcol_set= head->vcol_set; DBUG_ENTER("QUICK_RANGE_SELECT::reset"); last_range= NULL; cur_range= (QUICK_RANGE**) ranges.buffer; @@ -11392,8 +11580,7 @@ int QUICK_RANGE_SELECT::reset() } if (in_ror_merged_scan) - head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap, - &column_bitmap); + head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap); if (file->inited == handler::NONE) { @@ -11439,8 +11626,7 @@ int QUICK_RANGE_SELECT::reset() err: /* Restore bitmaps set on entry */ if (in_ror_merged_scan) - head->column_bitmaps_set_no_signal(save_read_set, save_write_set, - save_vcol_set); + head->column_bitmaps_set_no_signal(save_read_set, save_write_set); DBUG_RETURN(error); } @@ -11471,16 +11657,13 @@ int QUICK_RANGE_SELECT::get_next() MY_BITMAP * const save_read_set= head->read_set; MY_BITMAP * const save_write_set= head->write_set; - MY_BITMAP * const save_vcol_set= head->vcol_set; /* We don't need to signal the bitmap change as the bitmap is always the same for this head->file */ - head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap, - &column_bitmap); + head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap); result= file->multi_range_read_next(&dummy); - head->column_bitmaps_set_no_signal(save_read_set, save_write_set, - save_vcol_set); + head->column_bitmaps_set_no_signal(save_read_set, save_write_set); DBUG_RETURN(result); } @@ -13055,7 +13238,8 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item, if (args[0] && args[1]) // this is a binary function or BETWEEN { - DBUG_ASSERT(pred->is_bool_type()); + DBUG_ASSERT(pred->fixed_type_handler()); + DBUG_ASSERT(pred->fixed_type_handler()->is_bool_type()); Item_bool_func *bool_func= (Item_bool_func*) pred; Field *field= min_max_arg_item->field; if (!args[2]) // this is a binary function diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 13c0ce0c157..c4c30c9b50d 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -519,6 +519,7 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs, if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 !child_select->is_part_of_union() && // 1 parent_unit->first_select()->leaf_tables.elements && // 2 + child_select->outer_select() && child_select->outer_select()->leaf_tables.elements && // 2A subquery_types_allow_materialization(in_subs) && (in_subs->is_top_level_item() || //3 @@ -826,7 +827,7 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs) { DBUG_ENTER("subquery_types_allow_materialization"); - DBUG_ASSERT(in_subs->left_expr->fixed); + DBUG_ASSERT(in_subs->left_expr->is_fixed()); List_iterator<Item> it(in_subs->unit->first_select()->item_list); uint elements= in_subs->unit->first_select()->item_list.elements; @@ -902,7 +903,7 @@ bool make_in_exists_conversion(THD *thd, JOIN *join, Item_in_subselect *item) /* We're going to finalize IN->EXISTS conversion. Normally, IN->EXISTS conversion takes place inside the - Item_subselect::fix_fields() call, where item_subselect->fixed==FALSE (as + Item_subselect::fix_fields() call, where item_subselect->is_fixed()==FALSE (as fix_fields() haven't finished yet) and item_subselect->changed==FALSE (as the conversion haven't been finalized) @@ -929,7 +930,7 @@ bool make_in_exists_conversion(THD *thd, JOIN *join, Item_in_subselect *item) item->fixed= 1; Item *substitute= item->substitution; - bool do_fix_fields= !item->substitution->fixed; + bool do_fix_fields= !item->substitution->is_fixed(); /* The Item_subselect has already been wrapped with Item_in_optimizer, so we should search for item->optimizer, not 'item'. @@ -1265,7 +1266,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) in_subq->fixed= 1; Item *substitute= in_subq->substitution; - bool do_fix_fields= !in_subq->substitution->fixed; + bool do_fix_fields= !in_subq->substitution->is_fixed(); Item **tree= (in_subq->emb_on_expr_nest == NO_JOIN_NEST)? &join->conds : &(in_subq->emb_on_expr_nest->on_expr); Item *replace_me= in_subq->original_item(); @@ -1800,7 +1801,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) subq_lex->ref_pointer_array[i]); if (!item_eq) DBUG_RETURN(TRUE); - DBUG_ASSERT(subq_pred->left_expr->element_index(i)->fixed); + DBUG_ASSERT(subq_pred->left_expr->element_index(i)->is_fixed()); if (subq_pred->left_expr_orig->element_index(i) != subq_pred->left_expr->element_index(i)) thd->change_item_tree(item_eq->arguments(), @@ -5509,31 +5510,454 @@ int select_value_catcher::send_data(List<Item> &items) } -/* - Setup JTBM join tabs for execution +/** + @brief + Add new conditions after optimize_cond() call + + @param thd the thread handle + @param cond the condition where to attach new conditions + @param cond_eq IN/OUT the multiple equalities of cond + @param new_conds IN/OUT the list of conditions needed to add + @param cond_value the returned value of the condition + + @details + The method creates new condition through conjunction of cond and + the conditions from new_conds list. + The method is called after optimize_cond() for cond. The result + of the conjunction should be the same as if it was done before the + the optimize_cond() call. + + @retval NULL if an error occurs + @retval otherwise the created condition */ -bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, - Item **join_where) +Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond, + COND_EQUAL **cond_eq, + List<Item> &new_conds, + Item::cond_result *cond_value) +{ + COND_EQUAL new_cond_equal; + Item *item; + Item_equal *equality; + bool is_simplified_cond= false; + List_iterator<Item> li(new_conds); + List_iterator_fast<Item_equal> it(new_cond_equal.current_level); + + /* + Creates multiple equalities new_cond_equal from new_conds list + equalities. If multiple equality can't be created or the condition + from new_conds list isn't an equality the method leaves it in new_conds + list. + + The equality can't be converted into the multiple equality if it + is a knowingly false or true equality. + For example, (3 = 1) equality. + */ + while ((item=li++)) + { + if (item->type() == Item::FUNC_ITEM && + ((Item_func *) item)->functype() == Item_func::EQ_FUNC && + check_simple_equality(thd, + Item::Context(Item::ANY_SUBST, + ((Item_func_equal *)item)->compare_type_handler(), + ((Item_func_equal *)item)->compare_collation()), + ((Item_func *)item)->arguments()[0], + ((Item_func *)item)->arguments()[1], + &new_cond_equal)) + li.remove(); + } + + it.rewind(); + if (cond && cond->type() == Item::COND_ITEM && + ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) + { + /* + cond is an AND-condition. + The method conjugates the AND-condition cond, created multiple + equalities new_cond_equal and remain conditions from new_conds. + + First, the method disjoins multiple equalities of cond and + merges new_cond_equal multiple equalities with these equalities. + It checks if after the merge the multiple equalities are knowingly + true or false equalities. + It attaches to cond the conditions from new_conds list and the result + of the merge of multiple equalities. The multiple equalities are + attached only to the upper level of AND-condition cond. So they + should be pushed down to the inner levels of cond AND-condition + if needed. It is done by propagate_new_equalities(). + */ + COND_EQUAL *cond_equal= &((Item_cond_and *) cond)->m_cond_equal; + List<Item_equal> *cond_equalities= &cond_equal->current_level; + List<Item> *and_args= ((Item_cond_and *)cond)->argument_list(); + and_args->disjoin((List<Item> *) cond_equalities); + and_args->append(&new_conds); + + while ((equality= it++)) + { + equality->upper_levels= 0; + equality->merge_into_list(thd, cond_equalities, false, false); + } + List_iterator_fast<Item_equal> ei(*cond_equalities); + while ((equality= ei++)) + { + if (equality->const_item() && !equality->val_int()) + is_simplified_cond= true; + equality->fixed= 0; + if (equality->fix_fields(thd, NULL)) + return NULL; + } + + and_args->append((List<Item> *) cond_equalities); + *cond_eq= &((Item_cond_and *) cond)->m_cond_equal; + + propagate_new_equalities(thd, cond, cond_equalities, + cond_equal->upper_levels, + &is_simplified_cond); + cond= cond->propagate_equal_fields(thd, + Item::Context_boolean(), + cond_equal); + } + else + { + /* + cond isn't AND-condition or is NULL. + There can be several cases: + + 1. cond is a multiple equality. + In this case cond is merged with the multiple equalities of + new_cond_equal. + The new condition is created with the conjunction of new_conds + list conditions and the result of merge of multiple equalities. + 2. cond is NULL + The new condition is created from the conditions of new_conds + list and multiple equalities from new_cond_equal. + 3. Otherwise + In this case the new condition is created from cond, remain conditions + from new_conds list and created multiple equalities from + new_cond_equal. + */ + List<Item> new_conds_list; + /* Flag is set to true if cond is a multiple equality */ + bool is_mult_eq= (cond && cond->type() == Item::FUNC_ITEM && + ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC); + + if (cond && !is_mult_eq && + new_conds_list.push_back(cond, thd->mem_root)) + return NULL; + + if (new_conds.elements > 0) + { + li.rewind(); + while ((item=li++)) + { + if (item->fix_fields_if_needed(thd, NULL)) + return NULL; + if (item->const_item() && !item->val_int()) + is_simplified_cond= true; + } + + if (new_conds.elements > 1) + new_conds_list.append(&new_conds); + else + { + li.rewind(); + item= li++; + if (new_conds_list.push_back(item, thd->mem_root)) + return NULL; + } + } + + if (is_mult_eq) + { + Item_equal *eq_cond= (Item_equal *)cond; + eq_cond->upper_levels= 0; + eq_cond->merge_into_list(thd, &new_cond_equal.current_level, + false, false); + + while ((equality= it++)) + { + if (equality->const_item() && !equality->val_int()) + is_simplified_cond= true; + } + (*cond_eq)->copy(new_cond_equal); + } + + if (new_cond_equal.current_level.elements > 0) + { + if (new_cond_equal.current_level.elements + + new_conds_list.elements == 1) + { + it.rewind(); + equality= it++; + equality->fixed= 0; + if (equality->fix_fields(thd, NULL)) + return NULL; + } + new_conds_list.append((List<Item> *)&new_cond_equal.current_level); + } + + if (new_conds_list.elements > 1) + { + Item_cond_and *and_cond= + new (thd->mem_root) Item_cond_and(thd, new_conds_list); + + and_cond->m_cond_equal.copy(new_cond_equal); + cond= (Item *)and_cond; + *cond_eq= &((Item_cond_and *)cond)->m_cond_equal; + } + else + { + List_iterator_fast<Item> iter(new_conds_list); + cond= iter++; + } + + if (cond->fix_fields_if_needed(thd, NULL)) + return NULL; + + if (new_cond_equal.current_level.elements > 0) + cond= cond->propagate_equal_fields(thd, + Item::Context_boolean(), + &new_cond_equal); + } + + /* + If it was found that some of the created condition parts are knowingly + true or false equalities the method calls removes_eq_cond() to remove them + from cond and set the cond_value to the appropriate value. + */ + if (is_simplified_cond) + cond= cond->remove_eq_conds(thd, cond_value, true); + return cond; +} + + +/** + @brief Materialize a degenerate jtbm semi join + + @param thd thread handler + @param tbl table list for the target jtbm semi join table + @param subq_pred IN subquery predicate with the degenerate jtbm semi join + @param eq_list IN/OUT the list where to add produced equalities + + @details + The method materializes the degenerate jtbm semi join for the + subquery from the IN subquery predicate subq_pred taking table + as the target for materialization. + Any degenerate table is guaranteed to produce 0 or 1 record. + Examples of both cases: + + select * from ot where col in (select ... from it where 2>3) + select * from ot where col in (select MY_MIN(it.key) from it) + + in this case, there is no necessity to create a temp.table for + materialization. + We now just need to + 1. Check whether 1 or 0 records are produced, setup this as a + constant join tab. + 2. Create a dummy temporary table, because all of the join + optimization code relies on TABLE object being present. + + In the case when materialization produces one row the function + additionally creates equalities between the expressions from the + left part of the IN subquery predicate and the corresponding + columns of the produced row. These equalities are added to the + list eq_list. They are supposed to be conjuncted with the condition + of the WHERE clause. + + @retval TRUE if an error occurs + @retval FALSE otherwise +*/ + +bool execute_degenerate_jtbm_semi_join(THD *thd, + TABLE_LIST *tbl, + Item_in_subselect *subq_pred, + List<Item> &eq_list) +{ + DBUG_ENTER("execute_degenerate_jtbm_semi_join"); + select_value_catcher *new_sink; + + DBUG_ASSERT(subq_pred->engine->engine_type() == + subselect_engine::SINGLE_SELECT_ENGINE); + subselect_single_select_engine *engine= + (subselect_single_select_engine*)subq_pred->engine; + if (!(new_sink= new (thd->mem_root) select_value_catcher(thd, subq_pred))) + DBUG_RETURN(TRUE); + if (new_sink->setup(&engine->select_lex->join->fields_list) || + engine->select_lex->join->change_result(new_sink, NULL) || + engine->exec()) + { + DBUG_RETURN(TRUE); + } + subq_pred->is_jtbm_const_tab= TRUE; + + if (new_sink->assigned) + { + /* + Subselect produced one row, which is saved in new_sink->row. + Save "left_expr[i] == row[i]" equalities into the eq_list. + */ + subq_pred->jtbm_const_row_found= TRUE; + + Item *eq_cond; + for (uint i= 0; i < subq_pred->left_expr->cols(); i++) + { + eq_cond= + new (thd->mem_root) Item_func_eq(thd, + subq_pred->left_expr->element_index(i), + new_sink->row[i]); + if (!eq_cond || eq_cond->fix_fields(thd, NULL) || + eq_list.push_back(eq_cond, thd->mem_root)) + DBUG_RETURN(TRUE); + } + } + else + { + /* Subselect produced no rows. Just set the flag */ + subq_pred->jtbm_const_row_found= FALSE; + } + + TABLE *dummy_table; + if (!(dummy_table= create_dummy_tmp_table(thd))) + DBUG_RETURN(TRUE); + tbl->table= dummy_table; + tbl->table->pos_in_table_list= tbl; + /* + Note: the table created above may be freed by: + 1. JOIN_TAB::cleanup(), when the parent join is a regular join. + 2. cleanup_empty_jtbm_semi_joins(), when the parent join is a + degenerate join (e.g. one with "Impossible where"). + */ + setup_table_map(tbl->table, tbl, tbl->jtbm_table_no); + DBUG_RETURN(FALSE); +} + + +/** + @brief + Execute degenerate jtbm semi joins before optimize_cond() for parent + + @param join the parent join for jtbm semi joins + @param join_list the list of tables where jtbm semi joins are processed + @param eq_list IN/OUT the list where to add equalities produced after + materialization of single-row degenerate jtbm semi joins + + @details + The method traverses join_list trying to find any degenerate jtbm semi + joins for subqueries of IN predicates. For each degenerate jtbm + semi join execute_degenerate_jtbm_semi_join() is called. As a result + of this call new equalities that substitute for single-row materialized + jtbm semi join are added to eq_list. + + In the case when a table is nested in another table 'nested_join' the + method is recursively called for the join_list of the 'nested_join' trying + to find in the list any degenerate jtbm semi joins. Currently a jtbm semi + join may occur in a mergeable semi join nest. + + @retval TRUE if an error occurs + @retval FALSE otherwise +*/ + +bool setup_degenerate_jtbm_semi_joins(JOIN *join, + List<TABLE_LIST> *join_list, + List<Item> &eq_list) +{ + TABLE_LIST *table; + NESTED_JOIN *nested_join; + List_iterator<TABLE_LIST> li(*join_list); + THD *thd= join->thd; + DBUG_ENTER("setup_degenerate_jtbm_semi_joins"); + + while ((table= li++)) + { + Item_in_subselect *subq_pred; + + if ((subq_pred= table->jtbm_subselect)) + { + JOIN *subq_join= subq_pred->unit->first_select()->join; + + if (!subq_join->tables_list || !subq_join->table_count) + { + if (execute_degenerate_jtbm_semi_join(thd, + table, + subq_pred, + eq_list)) + DBUG_RETURN(TRUE); + join->is_orig_degenerated= true; + } + } + if ((nested_join= table->nested_join)) + { + if (setup_degenerate_jtbm_semi_joins(join, + &nested_join->join_list, + eq_list)) + DBUG_RETURN(TRUE); + } + } + DBUG_RETURN(FALSE); +} + + +/** + @brief + Optimize jtbm semi joins for materialization + + @param join the parent join for jtbm semi joins + @param join_list the list of TABLE_LIST objects where jtbm semi join + can occur + @param eq_list IN/OUT the list where to add produced equalities + + @details + This method is called by the optimizer after the call of + optimize_cond() for parent select. + The method traverses join_list trying to find any jtbm semi joins for + subqueries from IN predicates and optimizes them. + After the optimization some of jtbm semi joins may become degenerate. + For example the subquery 'SELECT MAX(b) FROM t2' from the query + + SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2); + + will become degenerate if there is an index on t2.b. + If a subquery becomes degenerate it is handled by the function + execute_degenerate_jtbm_semi_join(). + + Otherwise the method creates a temporary table in which the subquery + of the jtbm semi join will be materialied. + + The function saves the equalities between all pairs of the expressions + from the left part of the IN subquery predicate and the corresponding + columns of the subquery from the predicate in eq_list appending them + to the list. The equalities of eq_list will be later conjucted with the + condition of the WHERE clause. + + In the case when a table is nested in another table 'nested_join' the + method is recursively called for the join_list of the 'nested_join' trying + to find in the list any degenerate jtbm semi joins. Currently a jtbm semi + join may occur in a mergeable semi join nest. + + @retval TRUE if an error occurs + @retval FALSE otherwise +*/ + +bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, + List<Item> &eq_list) { TABLE_LIST *table; NESTED_JOIN *nested_join; List_iterator<TABLE_LIST> li(*join_list); THD *thd= join->thd; DBUG_ENTER("setup_jtbm_semi_joins"); - + while ((table= li++)) { - Item_in_subselect *item; + Item_in_subselect *subq_pred; - if ((item= table->jtbm_subselect)) + if ((subq_pred= table->jtbm_subselect)) { - Item_in_subselect *subq_pred= item; double rows; double read_time; /* - Perform optimization of the subquery, so that we know estmated + Perform optimization of the subquery, so that we know estimated - cost of materialization process - how many records will be in the materialized temp.table */ @@ -5546,102 +5970,37 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, if (!subq_join->tables_list || !subq_join->table_count) { - /* - A special case; subquery's join is degenerate, and it either produces - 0 or 1 record. Examples of both cases: - - select * from ot where col in (select ... from it where 2>3) - select * from ot where col in (select MY_MIN(it.key) from it) - - in this case, the subquery predicate has not been setup for - materialization. In particular, there is no materialized temp.table. - We'll now need to - 1. Check whether 1 or 0 records are produced, setup this as a - constant join tab. - 2. Create a dummy temporary table, because all of the join - optimization code relies on TABLE object being present (here we - follow a bad tradition started by derived tables) - */ - DBUG_ASSERT(subq_pred->engine->engine_type() == - subselect_engine::SINGLE_SELECT_ENGINE); - subselect_single_select_engine *engine= - (subselect_single_select_engine*)subq_pred->engine; - select_value_catcher *new_sink; - if (!(new_sink= - new (thd->mem_root) select_value_catcher(thd, subq_pred))) + if (!join->is_orig_degenerated && + execute_degenerate_jtbm_semi_join(thd, table, subq_pred, + eq_list)) DBUG_RETURN(TRUE); - if (new_sink->setup(&engine->select_lex->join->fields_list) || - engine->select_lex->join->change_result(new_sink, NULL) || - engine->exec()) - { - DBUG_RETURN(TRUE); - } - subq_pred->is_jtbm_const_tab= TRUE; - - if (new_sink->assigned) - { - subq_pred->jtbm_const_row_found= TRUE; - /* - Subselect produced one row, which is saved in new_sink->row. - Inject "left_expr[i] == row[i] equalities into parent's WHERE. - */ - Item *eq_cond; - for (uint i= 0; i < subq_pred->left_expr->cols(); i++) - { - eq_cond= new (thd->mem_root) - Item_func_eq(thd, subq_pred->left_expr->element_index(i), - new_sink->row[i]); - if (!eq_cond) - DBUG_RETURN(1); - - if (!((*join_where)= and_items(thd, *join_where, eq_cond)) || - (*join_where)->fix_fields(thd, join_where)) - DBUG_RETURN(1); - } - } - else - { - /* Subselect produced no rows. Just set the flag, */ - subq_pred->jtbm_const_row_found= FALSE; - } - - /* Set up a dummy TABLE*, optimizer code needs JOIN_TABs to have TABLE */ - TABLE *dummy_table; - if (!(dummy_table= create_dummy_tmp_table(thd))) - DBUG_RETURN(1); - table->table= dummy_table; - table->table->pos_in_table_list= table; - /* - Note: the table created above may be freed by: - 1. JOIN_TAB::cleanup(), when the parent join is a regular join. - 2. cleanup_empty_jtbm_semi_joins(), when the parent join is a - degenerate join (e.g. one with "Impossible where"). - */ - setup_table_map(table->table, table, table->jtbm_table_no); } else { DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION)); subq_pred->is_jtbm_const_tab= FALSE; subselect_hash_sj_engine *hash_sj_engine= - ((subselect_hash_sj_engine*)item->engine); + ((subselect_hash_sj_engine*)subq_pred->engine); table->table= hash_sj_engine->tmp_table; table->table->pos_in_table_list= table; setup_table_map(table->table, table, table->jtbm_table_no); - Item *sj_conds= hash_sj_engine->semi_join_conds; - - (*join_where)= and_items(thd, *join_where, sj_conds); - (*join_where)->fix_fields_if_needed(thd, join_where); + List_iterator<Item> li(*hash_sj_engine->semi_join_conds->argument_list()); + Item *item; + while ((item=li++)) + { + item->update_used_tables(); + if (eq_list.push_back(item, thd->mem_root)) + DBUG_RETURN(TRUE); + } } table->table->maybe_null= MY_TEST(join->mixed_implicit_grouping); } - if ((nested_join= table->nested_join)) { - if (setup_jtbm_semi_joins(join, &nested_join->join_list, join_where)) + if (setup_jtbm_semi_joins(join, &nested_join->join_list, eq_list)) DBUG_RETURN(TRUE); } } @@ -5749,8 +6108,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables) /* A strategy must be chosen earlier. */ DBUG_ASSERT(in_subs->has_strategy()); DBUG_ASSERT(in_to_exists_where || in_to_exists_having); - DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed); - DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed); + DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->is_fixed()); + DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->is_fixed()); /* The original QEP of the subquery. */ Join_plan_state save_qep(table_count); @@ -6037,3 +6396,418 @@ bool JOIN::choose_tableless_subquery_plan() exec_const_cond= zero_result_cause ? 0 : conds; return FALSE; } + + +/* + Check if the item exists in the fields list of the left part of + the IN subquery predicate subq_pred and returns its corresponding + item from the select of the right part of subq_pred. +*/ +Item *Item::get_corresponding_field_in_insubq(Item_in_subselect *subq_pred) +{ + DBUG_ASSERT(type() == Item::FIELD_ITEM || + (type() == Item::REF_ITEM && + ((Item_ref *) this)->ref_type() == Item_ref::VIEW_REF)); + + List_iterator<Field_pair> it(subq_pred->corresponding_fields); + Field_pair *ret; + Item_field *field_item= (Item_field *) (real_item()); + while ((ret= it++)) + { + if (field_item->field == ret->field) + return ret->corresponding_item; + } + return NULL; +} + + +bool Item_field::excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) +{ + if (((Item *)this)->get_corresponding_field_in_insubq(subq_pred)) + return true; + if (item_equal) + { + Item_equal_fields_iterator it(*item_equal); + Item *equal_item; + while ((equal_item= it++)) + { + if (equal_item->const_item()) + continue; + if (equal_item->get_corresponding_field_in_insubq(subq_pred)) + return true; + } + } + return false; +} + + +bool Item_direct_view_ref::excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) +{ + if (item_equal) + { + DBUG_ASSERT(real_item()->type() == Item::FIELD_ITEM); + if (((Item *)this)->get_corresponding_field_in_insubq(subq_pred)) + return true; + } + return (*ref)->excl_dep_on_in_subq_left_part(subq_pred); +} + + +bool Item_equal::excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) +{ + Item *left_item = get_const(); + Item_equal_fields_iterator it(*this); + Item *item; + if (!left_item) + { + while ((item=it++)) + { + if (item->excl_dep_on_in_subq_left_part(subq_pred)) + { + left_item= item; + break; + } + } + } + if (!left_item) + return false; + while ((item=it++)) + { + if (item->excl_dep_on_in_subq_left_part(subq_pred)) + return true; + } + return false; +} + + +/** + @brief + Get corresponding item from the select of the right part of IN subquery + + @param thd the thread handle + @param item the item from the left part of subq_pred for which + corresponding item should be found + @param subq_pred the IN subquery predicate + + @details + This method looks through the fields of the select of the right part of + the IN subquery predicate subq_pred trying to find the corresponding + item 'new_item' for item. If item has equal items it looks through + the fields of the select of the right part of subq_pred for each equal + item trying to find the corresponding item. + The method assumes that the given item is either a field item or + a reference to a field item. + + @retval <item*> reference to the corresponding item + @retval NULL if item was not found +*/ + +static +Item *get_corresponding_item(THD *thd, Item *item, + Item_in_subselect *subq_pred) +{ + DBUG_ASSERT(item->type() == Item::FIELD_ITEM || + (item->type() == Item::REF_ITEM && + ((Item_ref *) item)->ref_type() == Item_ref::VIEW_REF)); + + Item *corresonding_item; + Item_equal *item_equal= item->get_item_equal(); + + if (item_equal) + { + Item_equal_fields_iterator it(*item_equal); + Item *equal_item; + while ((equal_item= it++)) + { + corresonding_item= + equal_item->get_corresponding_field_in_insubq(subq_pred); + if (corresonding_item) + return corresonding_item; + } + return NULL; + } + else + return item->get_corresponding_field_in_insubq(subq_pred); +} + + +Item *Item_field::in_subq_field_transformer_for_where(THD *thd, uchar *arg) +{ + Item_in_subselect *subq_pred= (Item_in_subselect *)arg; + Item *producing_item= get_corresponding_item(thd, this, subq_pred); + if (producing_item) + return producing_item->build_clone(thd); + return this; +} + + +Item *Item_direct_view_ref::in_subq_field_transformer_for_where(THD *thd, + uchar *arg) +{ + if (item_equal) + { + Item_in_subselect *subq_pred= (Item_in_subselect *)arg; + Item *producing_item= get_corresponding_item(thd, this, subq_pred); + DBUG_ASSERT (producing_item != NULL); + return producing_item->build_clone(thd); + } + return this; +} + + +/** + @brief + Transforms item so it can be pushed into the IN subquery HAVING clause + + @param thd the thread handle + @param in_item the item for which pushable item should be created + @param subq_pred the IN subquery predicate + + @details + This method finds for in_item that is a field from the left part of the + IN subquery predicate subq_pred its corresponding item from the right part + of subq_pred. + If corresponding item is found, a shell for this item is created. + This shell can be pushed into the HAVING part of subq_pred select. + + @retval <item*> reference to the created corresponding item shell for in_item + @retval NULL if mistake occurs +*/ + +static Item* +get_corresponding_item_for_in_subq_having(THD *thd, Item *in_item, + Item_in_subselect *subq_pred) +{ + Item *new_item= get_corresponding_item(thd, in_item, subq_pred); + + if (new_item) + { + Item_ref *ref= + new (thd->mem_root) Item_ref(thd, + &subq_pred->unit->first_select()->context, + NullS, NullS, + &new_item->name); + if (!ref) + DBUG_ASSERT(0); + return ref; + } + return new_item; +} + + +Item *Item_field::in_subq_field_transformer_for_having(THD *thd, uchar *arg) +{ + return get_corresponding_item_for_in_subq_having(thd, this, + (Item_in_subselect *)arg); +} + + +Item *Item_direct_view_ref::in_subq_field_transformer_for_having(THD *thd, + uchar *arg) +{ + if (!item_equal) + return this; + else + { + Item *new_item= get_corresponding_item_for_in_subq_having(thd, this, + (Item_in_subselect *)arg); + if (!new_item) + return this; + return new_item; + } +} + + +/** + @brief + Find fields that are used in the GROUP BY of the select + + @param thd the thread handle + @param sel the select of the IN subquery predicate + @param fields fields of the left part of the IN subquery predicate + @param grouping_list GROUP BY clause + + @details + This method traverses fields which are used in the GROUP BY of + sel and saves them with their corresponding items from fields. +*/ + +bool grouping_fields_in_the_in_subq_left_part(THD *thd, + st_select_lex *sel, + List<Field_pair> *fields, + ORDER *grouping_list) +{ + DBUG_ENTER("grouping_fields_in_the_in_subq_left_part"); + sel->grouping_tmp_fields.empty(); + List_iterator<Field_pair> it(*fields); + Field_pair *item; + while ((item= it++)) + { + for (ORDER *ord= grouping_list; ord; ord= ord->next) + { + if ((*ord->item)->eq(item->corresponding_item, 0)) + { + if (sel->grouping_tmp_fields.push_back(item, thd->mem_root)) + DBUG_RETURN(TRUE); + } + } + } + DBUG_RETURN(FALSE); +} + + +/** + @brief + Extract condition that can be pushed into select of this IN subquery + + @param thd the thread handle + @param cond current condition + + @details + This function builds the most restrictive condition depending only on + the list of fields of the left part of this IN subquery predicate + (directly or indirectly through equality) that can be extracted from the + given condition cond and pushes it into this IN subquery. + + Example of the transformation: + + SELECT * FROM t1 + WHERE a>3 AND b>10 AND + (a,b) IN (SELECT x,MAX(y) FROM t2 GROUP BY x); + + => + + SELECT * FROM t1 + WHERE a>3 AND b>10 AND + (a,b) IN (SELECT x,max(y) + FROM t2 + WHERE x>3 + GROUP BY x + HAVING MAX(y)>10); + + + In details: + 1. Check what pushable formula can be extracted from cond + 2. Build a clone PC of the formula that can be extracted + (the clone is built only if the extracted formula is a AND subformula + of cond or conjunction of such subformulas) + 3. If there is no HAVING clause prepare PC to be conjuncted with + WHERE clause of this subquery. Otherwise do 4-7. + 4. Check what formula PC_where can be extracted from PC to be pushed + into the WHERE clause of the subquery + 5. Build PC_where and if PC_where is a conjunct(s) of PC remove it from PC + getting PC_having + 6. Prepare PC_where to be conjuncted with the WHERE clause of + the IN subquery + 7. Prepare PC_having to be conjuncted with the HAVING clause of + the IN subquery + + @note + This method is similar to pushdown_cond_for_derived() + + @retval TRUE if an error occurs + @retval FALSE otherwise +*/ + +bool Item_in_subselect::pushdown_cond_for_in_subquery(THD *thd, Item *cond) +{ + DBUG_ENTER("Item_in_subselect::pushdown_cond_for_in_subquery"); + Item *remaining_cond= NULL; + + if (!cond) + DBUG_RETURN(FALSE); + + st_select_lex *sel = unit->first_select(); + + if (is_jtbm_const_tab) + DBUG_RETURN(FALSE); + + if (!sel->cond_pushdown_is_allowed()) + DBUG_RETURN(FALSE); + + /* + Create a list of Field_pair items for this IN subquery. + It consists of the pairs of fields from the left part of this IN subquery + predicate 'left_part' and the respective fields from the select of the + right part of the IN subquery 'sel' (the field from left_part with the + corresponding field from the sel projection list). + Attach this list to the IN subquery. + */ + corresponding_fields.empty(); + List_iterator_fast<Item> it(sel->join->fields_list); + Item *item; + for (uint i= 0; i < left_expr->cols(); i++) + { + item= it++; + Item *elem= left_expr->element_index(i); + + if (elem->real_item()->type() != Item::FIELD_ITEM) + continue; + + if (corresponding_fields.push_back( + new Field_pair(((Item_field *)(elem->real_item()))->field, + item))) + DBUG_RETURN(TRUE); + } + + /* 1. Check what pushable formula can be extracted from cond */ + Item *extracted_cond; + cond->check_pushable_cond(&Item::pushable_cond_checker_for_subquery, + (uchar *)this); + /* 2. Build a clone PC of the formula that can be extracted */ + extracted_cond= + cond->build_pushable_cond(thd, + &Item::pushable_equality_checker_for_subquery, + (uchar *)this); + /* Nothing to push */ + if (!extracted_cond) + { + DBUG_RETURN(FALSE); + } + + /* Collect fields that are used in the GROUP BY of sel */ + st_select_lex *save_curr_select= thd->lex->current_select; + if (sel->have_window_funcs()) + { + if (sel->group_list.first || sel->join->implicit_grouping) + goto exit; + ORDER *common_partition_fields= + sel->find_common_window_func_partition_fields(thd); + if (!common_partition_fields) + goto exit; + + if (grouping_fields_in_the_in_subq_left_part(thd, sel, &corresponding_fields, + common_partition_fields)) + DBUG_RETURN(TRUE); + } + else if (grouping_fields_in_the_in_subq_left_part(thd, sel, + &corresponding_fields, + sel->group_list.first)) + DBUG_RETURN(TRUE); + + /* Do 4-6 */ + sel->pushdown_cond_into_where_clause(thd, extracted_cond, + &remaining_cond, + &Item::in_subq_field_transformer_for_where, + (uchar *) this); + if (!remaining_cond) + goto exit; + /* + 7. Prepare PC_having to be conjuncted with the HAVING clause of + the IN subquery + */ + remaining_cond= + remaining_cond->transform(thd, + &Item::in_subq_field_transformer_for_having, + (uchar *)this); + if (!remaining_cond) + goto exit; + + remaining_cond->walk(&Item::cleanup_excluding_const_fields_processor, + 0, 0); + sel->cond_pushed_into_having= remaining_cond; + +exit: + thd->lex->current_select= save_curr_select; + DBUG_RETURN(FALSE); +} diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 9cb19e0cc6c..031118288b9 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -26,8 +26,15 @@ int check_and_do_in_subquery_rewrites(JOIN *join); bool convert_join_subqueries_to_semijoins(JOIN *join); int pull_out_semijoin_tables(JOIN *join); bool optimize_semijoin_nests(JOIN *join, table_map all_table_map); -bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, - Item **join_where); +Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond, + COND_EQUAL **cond_eq, + List<Item> &new_conds, + Item::cond_result *cond_value); +bool setup_degenerate_jtbm_semi_joins(JOIN *join, + List<TABLE_LIST> *join_list, + List<Item> &eq_list); +bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, + List<Item> &eq_list); void cleanup_empty_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list); // used by Loose_scan_opt diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 82946709166..ecede5903a2 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -318,7 +318,7 @@ int opt_sum_query(THD *thd, error= tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); if (unlikely(error)) { - tl->table->file->print_error(error, MYF(ME_FATALERROR)); + tl->table->file->print_error(error, MYF(ME_FATAL)); DBUG_RETURN(error); } count*= tl->table->file->stats.records; diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index ef9b07cca47..74d1e775c43 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -617,12 +617,12 @@ void eliminate_tables(JOIN *join) we should also take into account tables mentioned in "val". */ if (join->thd->lex->sql_command == SQLCOM_INSERT_SELECT && - join->select_lex == &thd->lex->select_lex) + join->select_lex == thd->lex->first_select_lex()) { List_iterator<Item> val_it(thd->lex->value_list); while ((item= val_it++)) { - DBUG_ASSERT(item->fixed); + DBUG_ASSERT(item->is_fixed()); used_tables |= item->used_tables(); } } @@ -640,7 +640,7 @@ void eliminate_tables(JOIN *join) used_tables |= (*(cur_list->item))->used_tables(); } - if (join->select_lex == &thd->lex->select_lex) + if (join->select_lex == thd->lex->first_select_lex()) { /* Multi-table UPDATE: don't eliminate tables referred from SET statement */ diff --git a/sql/partition_info.h b/sql/partition_info.h index e00a2c44341..a0cde570d03 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -394,12 +394,13 @@ public: bool has_unique_name(partition_element *element); bool vers_init_info(THD *thd); - bool vers_set_interval(Item *item, interval_type int_type, my_time_t start) + bool vers_set_interval(THD *thd, Item *item, + interval_type int_type, my_time_t start) { DBUG_ASSERT(part_type == VERSIONING_PARTITION); vers_info->interval.type= int_type; vers_info->interval.start= start; - return get_interval_value(item, int_type, &vers_info->interval.step) || + return get_interval_value(thd, item, int_type, &vers_info->interval.step) || vers_info->interval.step.neg || vers_info->interval.step.second_part || !(vers_info->interval.step.year || vers_info->interval.step.month || vers_info->interval.step.day || vers_info->interval.step.hour || diff --git a/sql/procedure.h b/sql/procedure.h index 1ece31223ad..2169091c0a6 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -44,6 +44,16 @@ public: this->name.length= strlen(name_par); } enum Type type() const { return Item::PROC_ITEM; } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param) + { + /* + We can get to here when using a CURSOR for a query with PROCEDURE: + DECLARE c CURSOR FOR SELECT * FROM t1 PROCEDURE analyse(); + OPEN c; + */ + return create_tmp_field_ex_simple(table, src, param); + } virtual void set(double nr)=0; virtual void set(const char *str,uint length,CHARSET_INFO *cs)=0; virtual void set(longlong nr)=0; @@ -59,9 +69,9 @@ public: DBUG_ASSERT(0); // impossible return mark_unsupported_function("proc", arg, VCOL_IMPOSSIBLE); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date(thd, this, ltime, fuzzydate); } Item* get_copy(THD *thd) { return 0; } }; diff --git a/sql/protocol.cc b/sql/protocol.cc index c4c243ea166..7eee9283989 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -1195,9 +1195,8 @@ bool Protocol_text::store_decimal(const my_decimal *d) field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL); field_pos++; #endif - char buff[DECIMAL_MAX_STR_LENGTH]; - String str(buff, sizeof(buff), &my_charset_bin); - (void) my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str); + StringBuffer<DECIMAL_MAX_STR_LENGTH> str; + (void) d->to_string(&str); return net_store_data((uchar*) str.ptr(), str.length()); } @@ -1446,9 +1445,8 @@ bool Protocol_binary::store_decimal(const my_decimal *d) field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL); field_pos++; #endif - char buff[DECIMAL_MAX_STR_LENGTH]; - String str(buff, sizeof(buff), &my_charset_bin); - (void) my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str); + StringBuffer<DECIMAL_MAX_STR_LENGTH> str; + (void) d->to_string(&str); return store(str.ptr(), str.length(), str.charset()); } diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 6f659aa12ad..897d4394525 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -1091,7 +1091,7 @@ bool Master_info_index::init_all_master_info() if ((index_file_nr= my_open(index_file_name, O_RDWR | O_CREAT | O_BINARY , - MYF(MY_WME | ME_NOREFRESH))) < 0 || + MYF(MY_WME | ME_ERROR_LOG))) < 0 || my_sync(index_file_nr, MYF(MY_WME)) || init_io_cache(&index_file, index_file_nr, IO_SIZE, READ_CACHE, @@ -1307,7 +1307,7 @@ Master_info *get_master_info(const LEX_CSTRING *connection_name, if (warning != Sql_condition::WARN_LEVEL_NOTE) my_error(WARN_NO_MASTER_INFO, MYF(warning == Sql_condition::WARN_LEVEL_WARN ? - ME_JUST_WARNING : 0), + ME_WARNING : 0), (int) connection_name->length, connection_name->str); mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(0); @@ -1377,7 +1377,7 @@ Master_info_index::get_master_info(const LEX_CSTRING *connection_name, if (!mi && warning != Sql_condition::WARN_LEVEL_NOTE) { my_error(WARN_NO_MASTER_INFO, - MYF(warning == Sql_condition::WARN_LEVEL_WARN ? ME_JUST_WARNING : + MYF(warning == Sql_condition::WARN_LEVEL_WARN ? ME_WARNING : 0), (int) connection_name->length, connection_name->str); diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index db579a63ce0..94c1f08e4e3 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -497,7 +497,9 @@ int prepare_record(TABLE *const table, const uint skip, const bool check) DBUG_RETURN(0); } /** - Fills @c table->record[0] with computed values of extra persistent column which are present on slave but not on master. + Fills @c table->record[0] with computed values of extra persistent column + which are present on slave but not on master. + @param table Table whose record[0] buffer is prepared. @param master_cols No of columns on master @returns 0 on success @@ -514,10 +516,8 @@ int fill_extra_persistent_columns(TABLE *table, int master_cols) vfield= *vfield_ptr; if (vfield->field_index >= master_cols && vfield->stored_in_db()) { - /*Set bitmap for writing*/ - bitmap_set_bit(table->vcol_set, vfield->field_index); + bitmap_set_bit(table->write_set, vfield->field_index); error= vfield->vcol_info->expr->save_in_field(vfield,0); - bitmap_clear_bit(table->vcol_set, vfield->field_index); } } return error; diff --git a/sql/set_var.cc b/sql/set_var.cc index 8ab892068b3..de9bda3d067 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -742,7 +742,7 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free) err: if (free) - free_underlaid_joins(thd, &thd->lex->select_lex); + free_underlaid_joins(thd, thd->lex->first_select_lex()); DBUG_RETURN(error); } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index a60ddeb3017..e73666cfb58 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6570,7 +6570,7 @@ ER_ACCESS_DENIED_NO_PASSWORD_ERROR 28000 ukr "Доступ заборонено для користувача: '%s'@'%s'" ER_SET_PASSWORD_AUTH_PLUGIN - eng "SET PASSWORD has no significance for users authenticating via plugins" + eng "SET PASSWORD is ignored for users authenticating via %s plugin" ER_GRANT_PLUGIN_USER_EXISTS eng "GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists" diff --git a/sql/sp.cc b/sql/sp.cc index af86737ebb9..723f30ec85d 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1468,7 +1468,7 @@ log: log_query.ptr(), log_query.length(), FALSE, FALSE, FALSE, 0)) { - my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), "binary log", -1); + my_error(ER_ERROR_ON_WRITE, MYF(0), "binary log", -1); goto done; } thd->variables.sql_mode= 0; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index c1c938dd9e7..c86edc47bf9 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -71,33 +71,6 @@ static void reset_start_time_for_sp(THD *thd) } -Item::Type -sp_map_item_type(const Type_handler *handler) -{ - if (handler == &type_handler_row) - return Item::ROW_ITEM; - enum_field_types type= real_type_to_type(handler->real_field_type()); - - switch (type) { - case MYSQL_TYPE_BIT: - case MYSQL_TYPE_TINY: - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_LONGLONG: - case MYSQL_TYPE_INT24: - return Item::INT_ITEM; - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - return Item::DECIMAL_ITEM; - case MYSQL_TYPE_FLOAT: - case MYSQL_TYPE_DOUBLE: - return Item::REAL_ITEM; - default: - return Item::STRING_ITEM; - } -} - - bool Item_splocal::append_for_log(THD *thd, String *str) { if (fix_fields_if_needed(thd, NULL)) @@ -318,7 +291,7 @@ sp_get_flags_for_command(LEX *lex) - EXPLAIN DELETE ... - ANALYZE DELETE ... */ - if (lex->select_lex.item_list.is_empty() && + if (lex->first_select_lex()->item_list.is_empty() && !lex->describe && !lex->analyze_stmt) flags= 0; else @@ -1922,7 +1895,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, for (arg_no= 0; arg_no < argcount; arg_no++) { /* Arguments must be fixed in Item_func_sp::fix_fields */ - DBUG_ASSERT(argp[arg_no]->fixed); + DBUG_ASSERT(argp[arg_no]->is_fixed()); if ((err_status= (*func_ctx)->set_parameter(thd, arg_no, &(argp[arg_no])))) goto err_with_cleanup; @@ -2292,6 +2265,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (!err_status) { err_status= execute(thd, TRUE); + DBUG_PRINT("info", ("execute returned %d", (int) err_status)); } if (save_log_general) @@ -4576,7 +4550,7 @@ sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp) thd->spcont->set_case_expr(thd, m_case_expr_id, &null_item)) { /* If this also failed, we have to abort. */ - my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); + my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL)); } } else diff --git a/sql/sp_head.h b/sql/sp_head.h index cf934603cf0..8db6ecac9e7 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -39,9 +39,6 @@ @{ */ -Item::Type -sp_map_item_type(const Type_handler *handler); - uint sp_get_flags_for_command(LEX *lex); @@ -592,7 +589,8 @@ public: if (!oldlex) DBUG_RETURN(false); // Nothing to restore LEX *sublex= thd->lex; - if (thd->restore_from_local_lex_to_old_lex(oldlex))// This restores thd->lex + // This restores thd->lex and thd->stmt_lex + if (thd->restore_from_local_lex_to_old_lex(oldlex)) DBUG_RETURN(true); if (!sublex->sp_lex_in_use) { diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 24777abe1c3..a31631e33ef 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -228,9 +228,10 @@ bool Qualified_column_ident::resolve_type_ref(THD *thd, Column_definition *def) // Make %TYPE variables see temporary tables that shadow permanent tables thd->temporary_tables= open_tables_state_backup.temporary_tables; - if ((table_list= lex.select_lex.add_table_to_list(thd, this, NULL, 0, - TL_READ_NO_INSERT, - MDL_SHARED_READ)) && + if ((table_list= + lex.first_select_lex()->add_table_to_list(thd, this, NULL, 0, + TL_READ_NO_INSERT, + MDL_SHARED_READ)) && !check_table_access(thd, SELECT_ACL, table_list, TRUE, UINT_MAX, FALSE) && !open_tables_only_view_structure(thd, table_list, thd->mdl_context.has_locks())) @@ -286,9 +287,10 @@ bool Table_ident::resolve_table_rowtype_ref(THD *thd, // Make %ROWTYPE variables see temporary tables that shadow permanent tables thd->temporary_tables= open_tables_state_backup.temporary_tables; - if ((table_list= lex.select_lex.add_table_to_list(thd, this, NULL, 0, - TL_READ_NO_INSERT, - MDL_SHARED_READ)) && + if ((table_list= + lex.first_select_lex()->add_table_to_list(thd, this, NULL, 0, + TL_READ_NO_INSERT, + MDL_SHARED_READ)) && !check_table_access(thd, SELECT_ACL, table_list, TRUE, UINT_MAX, FALSE) && !open_tables_only_view_structure(thd, table_list, thd->mdl_context.has_locks())) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 3727bf7d7ce..052c5ada3a2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -57,6 +57,8 @@ #include "sql_plugin_compat.h" +#define MAX_SCRAMBLE_LENGTH 1024 + bool mysql_user_table_is_in_short_password_format= false; static LEX_CSTRING native_password_plugin_name= { @@ -85,11 +87,19 @@ LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") }; LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") }; -#ifndef NO_EMBEDDED_ACCESS_CHECKS static plugin_ref old_password_plugin; -#endif static plugin_ref native_password_plugin; +static plugin_ref get_auth_plugin(THD *thd, const LEX_CSTRING &name, bool *locked) +{ + if (name.str == native_password_plugin_name.str) + return native_password_plugin; + else if (name.str == old_password_plugin_name.str) + return old_password_plugin; + *locked=true; + return my_plugin_lock_by_name(thd, &name, MYSQL_AUTHENTICATION_PLUGIN); +} + /* Classes */ struct acl_host_and_ip @@ -119,13 +129,10 @@ public: char *db; }; -class ACL_USER_BASE :public ACL_ACCESS +class ACL_USER_BASE :public ACL_ACCESS, public Sql_alloc { public: - static void *operator new(size_t size, MEM_ROOT *mem_root) - { return (void*) alloc_root(mem_root, size); } - static void operator delete(void *, MEM_ROOT *){} uchar flags; // field used to store various state information LEX_CSTRING user; /* list to hold references to granted roles (ACL_ROLE instances) */ @@ -138,13 +145,12 @@ public: acl_host_and_ip host; size_t hostname_length; USER_RESOURCES user_resource; - uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form - uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 enum SSL_type ssl_type; const char *ssl_cipher, *x509_issuer, *x509_subject; LEX_CSTRING plugin; LEX_CSTRING auth_string; LEX_CSTRING default_rolename; + LEX_CSTRING salt; ACL_USER *copy(MEM_ROOT *root) { @@ -152,8 +158,7 @@ public: if (!dst) return 0; *dst= *this; - dst->user.str= safe_strdup_root(root, user.str); - dst->user.length= user.length; + dst->user= safe_lexcstrdup_root(root, user); dst->ssl_cipher= safe_strdup_root(root, ssl_cipher); dst->x509_issuer= safe_strdup_root(root, x509_issuer); dst->x509_subject= safe_strdup_root(root, x509_subject); @@ -161,11 +166,11 @@ public: plugin.str == old_password_plugin_name.str) dst->plugin= plugin; else - dst->plugin.str= strmake_root(root, plugin.str, plugin.length); - dst->auth_string.str= safe_strdup_root(root, auth_string.str); + dst->plugin= safe_lexcstrdup_root(root, plugin); + dst->auth_string= safe_lexcstrdup_root(root, auth_string); + dst->salt= safe_lexcstrdup_root(root, salt); dst->host.hostname= safe_strdup_root(root, host.hostname); - dst->default_rolename.str= safe_strdup_root(root, default_rolename.str); - dst->default_rolename.length= default_rolename.length; + dst->default_rolename= safe_lexcstrdup_root(root, default_rolename); bzero(&dst->role_grants, sizeof(role_grants)); return dst; } @@ -174,7 +179,7 @@ public: { CHARSET_INFO *cs= system_charset_info; int res; - res= strcmp(safe_str(user.str), safe_str(user2)); + res= strcmp(user.str, user2); if (!res) res= my_strcasecmp(cs, host.hostname, host2); return res; @@ -184,7 +189,7 @@ public: bool wild_eq(const char *user2, const char *host2, const char *ip2) { - if (strcmp(safe_str(user.str), safe_str(user2))) + if (strcmp(user.str, user2)) return false; return compare_hostname(&host, host2, ip2 ? ip2 : host2); @@ -275,10 +280,9 @@ public: const char *proxied_host_arg, const char *proxied_user_arg, bool with_grant_arg) { - user= (user_arg && *user_arg) ? user_arg : NULL; + user= user_arg; update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL); - proxied_user= (proxied_user_arg && *proxied_user_arg) ? - proxied_user_arg : NULL; + proxied_user= proxied_user_arg; update_hostname (&proxied_host, (proxied_host_arg && *proxied_host_arg) ? proxied_host_arg : NULL); @@ -291,11 +295,10 @@ public: bool with_grant_arg) { init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL, - (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL, + strdup_root (mem, user_arg), (proxied_host_arg && *proxied_host_arg) ? strdup_root (mem, proxied_host_arg) : NULL, - (proxied_user_arg && *proxied_user_arg) ? - strdup_root (mem, proxied_user_arg) : NULL, + strdup_root (mem, proxied_user_arg), with_grant_arg); } @@ -308,7 +311,7 @@ public: const char *get_proxied_host() { return proxied_host.hostname; } void set_user(MEM_ROOT *mem, const char *user_arg) { - user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL; + user= *user_arg ? strdup_root(mem, user_arg) : ""; } void set_host(MEM_ROOT *mem, const char *host_arg) { @@ -323,9 +326,8 @@ public: { sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' " "ignored in --skip-name-resolve mode.", - safe_str(proxied_user), - safe_str(proxied_host.hostname), - safe_str(user), + proxied_user, + safe_str(proxied_host.hostname), user, safe_str(host.hostname)); return TRUE; } @@ -345,11 +347,10 @@ public: proxied_user_arg, proxied_user)); DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) && compare_hostname(&proxied_host, host_arg, ip_arg) && - (!user || + (!*user || (user_arg && !wild_compare(user_arg, user, TRUE))) && - (!proxied_user || - (proxied_user && !wild_compare(proxied_user_arg, - proxied_user, TRUE)))); + (!*proxied_user || + !wild_compare(proxied_user_arg, proxied_user, TRUE))); } @@ -381,8 +382,7 @@ public: bool granted_on(const char *host_arg, const char *user_arg) { - return (((!user && (!user_arg || !user_arg[0])) || - (user && user_arg && !strcmp(user, user_arg))) && + return (!strcmp(user, user_arg) && ((!host.hostname && (!host_arg || !host_arg[0])) || (host.hostname && host_arg && !strcmp(host.hostname, host_arg)))); } @@ -391,17 +391,15 @@ public: void print_grant(String *str) { str->append(STRING_WITH_LEN("GRANT PROXY ON '")); - if (proxied_user) - str->append(proxied_user, strlen(proxied_user)); + str->append(proxied_user); str->append(STRING_WITH_LEN("'@'")); if (proxied_host.hostname) str->append(proxied_host.hostname, strlen(proxied_host.hostname)); str->append(STRING_WITH_LEN("' TO '")); - if (user) - str->append(user, strlen(user)); + str->append(user); str->append(STRING_WITH_LEN("'@'")); if (host.hostname) - str->append(host.hostname, strlen(host.hostname)); + str->append(host.hostname); str->append(STRING_WITH_LEN("'")); if (with_grant) str->append(STRING_WITH_LEN(" WITH GRANT OPTION")); @@ -622,8 +620,8 @@ static ACL_USER *find_user_wild(const char *host, const char *user, const char * static ACL_ROLE *find_acl_role(const char *user); static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r); static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host); -static bool update_user_table(THD *, const User_table &, const char *, const char *, const - char *, size_t new_password_len); +static bool update_user_table_password(THD *, const User_table&, + const ACL_USER &); static bool acl_load(THD *thd, const Grant_tables& grant_tables); static inline void get_grantor(THD *thd, char* grantor); static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname); @@ -1306,9 +1304,9 @@ void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table, MEM_ROOT *mem) { init(get_field(mem, proxies_priv_table.host()), - get_field(mem, proxies_priv_table.user()), + safe_str(get_field(mem, proxies_priv_table.user())), get_field(mem, proxies_priv_table.proxied_host()), - get_field(mem, proxies_priv_table.proxied_user()), + safe_str(get_field(mem, proxies_priv_table.proxied_user())), proxies_priv_table.with_grant()->val_int() != 0); } @@ -1337,8 +1335,7 @@ ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0) access= user->access; /* set initial role access the same as the table row privileges */ initial_role_access= user->access; - this->user.str= safe_strdup_root(root, user->user.str); - this->user.length= user->user.length; + this->user= user->user; bzero(&role_grants, sizeof(role_grants)); bzero(&parent_grantee, sizeof(parent_grantee)); flags= IS_ROLE; @@ -1388,7 +1385,7 @@ static bool has_validation_plugins() MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL); } -struct validation_data { LEX_CSTRING *user, *password; }; +struct validation_data { const LEX_CSTRING *user, *password; }; static my_bool do_validate(THD *, plugin_ref plugin, void *arg) { @@ -1399,13 +1396,13 @@ static my_bool do_validate(THD *, plugin_ref plugin, void *arg) } -static bool validate_password(LEX_USER *user, THD *thd) +static bool validate_password(THD *thd, const LEX_CSTRING &user, + const LEX_CSTRING &pwtext, bool has_hash) { - if (user->pwtext.length || !user->pwhash.length) + if (pwtext.length || !has_hash) { - struct validation_data data= { &user->user, - user->pwtext.str ? &user->pwtext : - const_cast<LEX_CSTRING *>(&empty_clex_str) }; + struct validation_data data= { &user, + pwtext.str ? &pwtext : &empty_clex_str }; if (plugin_foreach(NULL, do_validate, MariaDB_PASSWORD_VALIDATION_PLUGIN, &data)) { @@ -1426,45 +1423,87 @@ static bool validate_password(LEX_USER *user, THD *thd) } /** - Convert scrambled password to binary form, according to scramble type, - Binary form is stored in user.salt. - - @param acl_user The object where to store the salt - @param password The password hash containing the salt - @param password_len The length of the password hash - - Despite the name of the function it is used when loading ACLs from disk - to store the password hash in the ACL_USER object. -*/ + Fills in ACL_USER::auth_string and ACL_USER::salt fields, as needed -static void -set_user_salt(ACL_USER *acl_user, const char *password, size_t password_len) + hashes the plain-text password (if provided) to auth_string, + converts auth_string to salt. + + Fails if the plain-text password fails validation, if the plugin is + not loaded, if the auth_string is invalid. + + Using NULL for a password disables validation + (needed for loading from mysql.user table). +*/ +static int set_user_auth(THD *thd, ACL_USER *acl_user, const LEX_CSTRING *pwtext) { - if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH) + const char *plugin_name= acl_user->plugin.str; + bool unlock_plugin= false; + plugin_ref plugin= get_auth_plugin(thd, acl_user->plugin, &unlock_plugin); + int res= 1; + + if (!plugin) { - get_salt_from_password(acl_user->salt, password); - acl_user->salt_len= SCRAMBLE_LENGTH; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_PLUGIN_IS_NOT_LOADED, + ER_THD(thd, ER_PLUGIN_IS_NOT_LOADED), plugin_name); + return res; } - else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + + acl_user->salt= acl_user->auth_string; + + st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info; + if (auth->interface_version >= 0x0202) { - get_salt_from_password_323((ulong *) acl_user->salt, password); - acl_user->salt_len= SCRAMBLE_LENGTH_323; + if (pwtext) + { + if (auth->hash_password && + validate_password(thd, acl_user->user, *pwtext, + acl_user->auth_string.length)) + goto end; + if (pwtext->length) + { + if (auth->hash_password) + { + char buf[MAX_SCRAMBLE_LENGTH]; + size_t len= sizeof(buf) - 1; + if (auth->hash_password(pwtext->str, pwtext->length, buf, &len)) + goto end; // OOM? + buf[len] = 0; + acl_user->auth_string.str= (char*)memdup_root(&acl_memroot, buf, len+1); + acl_user->auth_string.length= len; + } + else + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_SET_PASSWORD_AUTH_PLUGIN, + ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN), + acl_user->plugin.str); + } + } + } + + if (acl_user->auth_string.length) + { + if (auth->preprocess_hash) + { + uchar buf[MAX_SCRAMBLE_LENGTH]; + size_t len= sizeof(buf); + if (auth->preprocess_hash(acl_user->auth_string.str, + acl_user->auth_string.length, buf, &len)) + goto end; // ER_PASSWD_LENGTH? + acl_user->salt.str= (char*)memdup_root(&acl_memroot, buf, len); + acl_user->salt.length= len; + } + else + acl_user->salt= acl_user->auth_string; + } } - else - acl_user->salt_len= 0; -} -static const char *fix_plugin_ptr(const char *name) -{ - if (my_strcasecmp(system_charset_info, name, - native_password_plugin_name.str) == 0) - return native_password_plugin_name.str; - else - if (my_strcasecmp(system_charset_info, name, - old_password_plugin_name.str) == 0) - return old_password_plugin_name.str; - else - return name; + res= 0; +end: + if (unlock_plugin) + plugin_unlock(thd, plugin); + return res; } /** @@ -1475,8 +1514,6 @@ static const char *fix_plugin_ptr(const char *name) authentication, we want to be able to detect built-ins by comparing pointers, not strings. - Additionally - update the salt if the plugin is built-in. - @retval 0 the pointers were fixed @retval 1 this ACL_USER uses a not built-in plugin */ @@ -1489,98 +1526,6 @@ static bool fix_user_plugin_ptr(ACL_USER *user) user->plugin= old_password_plugin_name; else return true; - - if (user->auth_string.length) - set_user_salt(user, user->auth_string.str, user->auth_string.length); - return false; -} - - -/* - Validates the password, calculates password hash, transforms - equivalent LEX_USER representations. - - Upon entering this function: - - - if user->plugin is specified, user->auth is the plugin auth data. - - if user->plugin is mysql_native_password or mysql_old_password, - user->auth is the password hash, and LEX_USER is transformed - to match the next case (that is, user->plugin is cleared). - - if user->plugin is NOT specified, built-in auth is assumed, that is - mysql_native_password or mysql_old_password. In that case, - user->pwhash is the password hash. And user->pwtext is the original - plain-text password. Either one can be set or both. - - Upon exiting this function: - - - user->pwtext is left untouched - - user->pwhash is the password hash, as the mysql.user.password column - - user->plugin is the plugin name, as the mysql.user.plugin column - - user->auth is the plugin auth data, as the mysql.user.authentication_string column -*/ -static bool fix_lex_user(THD *thd, LEX_USER *user) -{ - size_t check_length; - - DBUG_ASSERT(user->plugin.length || !user->auth.length); - DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length))); - - if (lex_string_eq(&user->plugin, &native_password_plugin_name)) - check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; - else - if (lex_string_eq(&user->plugin, &old_password_plugin_name)) - check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; - else - if (user->plugin.length) - return false; // nothing else to do - else if (thd->variables.old_passwords == 1 || - user->pwhash.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) - check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; - else - check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; - - if (user->plugin.length) - { - user->pwhash= user->auth; - user->plugin= empty_clex_str; - user->auth= empty_clex_str; - } - - if (user->pwhash.length && user->pwhash.length != check_length) - { - my_error(ER_PASSWD_LENGTH, MYF(0), (int) check_length); - return true; - } - - if (user->pwtext.length && !user->pwhash.length) - { - size_t scramble_length; - void (*make_scramble)(char *, const char *, size_t); - - if (thd->variables.old_passwords == 1) - { - scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; - make_scramble= my_make_scrambled_password_323; - } - else - { - scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; - make_scramble= my_make_scrambled_password; - } - - Query_arena *arena, backup; - arena= thd->activate_stmt_arena_if_needed(&backup); - char *buff= (char *) thd->alloc(scramble_length + 1); - if (arena) - thd->restore_active_arena(arena, &backup); - - if (buff == NULL) - return true; - make_scramble(buff, user->pwtext.str, user->pwtext.length); - user->pwhash.str= buff; - user->pwhash.length= scramble_length; - } - return false; } @@ -1661,23 +1606,22 @@ bool acl_init(bool dont_read_acl_tables) Choose from either native or old password plugins when assigning a password */ -static bool set_user_plugin (ACL_USER *user, size_t password_len) +static LEX_CSTRING &guess_auth_plugin(THD *thd, size_t password_len) { - switch (password_len) - { - case 0: /* no password */ - case SCRAMBLED_PASSWORD_CHAR_LENGTH: - user->plugin= native_password_plugin_name; - return FALSE; - case SCRAMBLED_PASSWORD_CHAR_LENGTH_323: - user->plugin= old_password_plugin_name; - return FALSE; - default: - sql_print_warning("Found invalid password for user: '%s@%s'; " - "Ignoring user", safe_str(user->user.str), - safe_str(user->host.hostname)); - return TRUE; - } + if (thd->variables.old_passwords == 1 || + password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + return old_password_plugin_name; + else + return native_password_plugin_name; +} + + +static void push_new_user(const ACL_USER &user) +{ + push_dynamic(&acl_users, &user); + if (!user.host.hostname || + (user.host.hostname[0] == wild_many && !user.host.hostname[1])) + allow_all_hosts=1; // Anyone can connect } @@ -1702,7 +1646,6 @@ static bool acl_load(THD *thd, const Grant_tables& tables) READ_RECORD read_record_info; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; char tmp_name[SAFE_NAME_LEN+1]; - int password_length; Sql_mode_save old_mode_save(thd); DBUG_ENTER("acl_load"); @@ -1775,8 +1718,8 @@ static bool acl_load(THD *thd, const Grant_tables& tables) USERNAME_CHAR_LENGTH); if (user_table.password()) // Password column might be missing. (MySQL 5.7.6+) { - password_length= user_table.password()->field_length / - user_table.password()->charset()->mbmaxlen; + int password_length= user_table.password()->field_length / + user_table.password()->charset()->mbmaxlen; if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) { sql_print_error("Fatal error: mysql.user table is damaged or in " @@ -1826,9 +1769,9 @@ static bool acl_load(THD *thd, const Grant_tables& tables) bool is_role= FALSE; bzero(&user, sizeof(user)); update_hostname(&user.host, get_field(&acl_memroot, user_table.host())); - char *username= get_field(&acl_memroot, user_table.user()); + char *username= safe_str(get_field(&acl_memroot, user_table.user())); user.user.str= username; - user.user.length= safe_strlen(username); + user.user.length= strlen(username); /* If the user entry is a role, skip password and hostname checks @@ -1846,188 +1789,189 @@ static bool acl_load(THD *thd, const Grant_tables& tables) hostname_requires_resolving(user.host.hostname)) { sql_print_warning("'user' entry '%s@%s' " - "ignored in --skip-name-resolve mode.", - safe_str(user.user.str), + "ignored in --skip-name-resolve mode.", user.user.str, safe_str(user.host.hostname)); continue; } - char *password= const_cast<char*>(""); if (user_table.password()) - password= get_field(&acl_memroot, user_table.password()); - size_t password_len= safe_strlen(password); - user.auth_string.str= safe_str(password); - user.auth_string.length= password_len; - set_user_salt(&user, password, password_len); + { + const char *p= safe_str(get_field(&acl_memroot, user_table.password())); + user.auth_string.str= p; + user.auth_string.length= strlen(p); + } + else + user.auth_string= empty_clex_str; - if (!is_role && set_user_plugin(&user, password_len)) - continue; + user.plugin= guess_auth_plugin(thd, user.auth_string.length); - { - user.access= user_table.get_access() & GLOBAL_ACLS; - /* - if it is pre 5.0.1 privilege table then map CREATE privilege on - CREATE VIEW & SHOW VIEW privileges - */ - if (user_table.num_fields() <= 31 && (user.access & CREATE_ACL)) - user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL); + user.access= user_table.get_access() & GLOBAL_ACLS; + /* + if it is pre 5.0.1 privilege table then map CREATE privilege on + CREATE VIEW & SHOW VIEW privileges + */ + if (user_table.num_fields() <= 31 && (user.access & CREATE_ACL)) + user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL); - /* - if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on - CREATE PROCEDURE & ALTER PROCEDURE privileges - */ - if (user_table.num_fields() <= 33 && (user.access & CREATE_ACL)) - user.access|= CREATE_PROC_ACL; - if (user_table.num_fields() <= 33 && (user.access & ALTER_ACL)) - user.access|= ALTER_PROC_ACL; + /* + if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on + CREATE PROCEDURE & ALTER PROCEDURE privileges + */ + if (user_table.num_fields() <= 33 && (user.access & CREATE_ACL)) + user.access|= CREATE_PROC_ACL; + if (user_table.num_fields() <= 33 && (user.access & ALTER_ACL)) + user.access|= ALTER_PROC_ACL; - /* - pre 5.0.3 did not have CREATE_USER_ACL - */ - if (user_table.num_fields() <= 36 && (user.access & GRANT_ACL)) - user.access|= CREATE_USER_ACL; + /* + pre 5.0.3 did not have CREATE_USER_ACL + */ + if (user_table.num_fields() <= 36 && (user.access & GRANT_ACL)) + user.access|= CREATE_USER_ACL; - /* - if it is pre 5.1.6 privilege table then map CREATE privilege on - CREATE|ALTER|DROP|EXECUTE EVENT - */ - if (user_table.num_fields() <= 37 && (user.access & SUPER_ACL)) - user.access|= EVENT_ACL; + /* + if it is pre 5.1.6 privilege table then map CREATE privilege on + CREATE|ALTER|DROP|EXECUTE EVENT + */ + if (user_table.num_fields() <= 37 && (user.access & SUPER_ACL)) + user.access|= EVENT_ACL; - /* - if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE. - */ - if (user_table.num_fields() <= 38 && (user.access & SUPER_ACL)) - user.access|= TRIGGER_ACL; + /* + if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE. + */ + if (user_table.num_fields() <= 38 && (user.access & SUPER_ACL)) + user.access|= TRIGGER_ACL; - if (user_table.num_fields() <= 46 && (user.access & DELETE_ACL)) - user.access|= DELETE_HISTORY_ACL; + if (user_table.num_fields() <= 46 && (user.access & DELETE_ACL)) + user.access|= DELETE_HISTORY_ACL; - user.sort= get_sort(2, user.host.hostname, user.user.str); - user.hostname_length= safe_strlen(user.host.hostname); - user.user_resource.user_conn= 0; - user.user_resource.max_statement_time= 0.0; + user.sort= get_sort(2, user.host.hostname, user.user.str); + user.hostname_length= safe_strlen(user.host.hostname); + user.user_resource.user_conn= 0; + user.user_resource.max_statement_time= 0.0; - /* Starting from 4.0.2 we have more fields */ - if (user_table.ssl_type()) + /* Starting from 4.0.2 we have more fields */ + if (user_table.ssl_type()) + { + char *ssl_type=get_field(thd->mem_root, user_table.ssl_type()); + if (!ssl_type) + user.ssl_type=SSL_TYPE_NONE; + else if (!strcmp(ssl_type, "ANY")) + user.ssl_type=SSL_TYPE_ANY; + else if (!strcmp(ssl_type, "X509")) + user.ssl_type=SSL_TYPE_X509; + else /* !strcmp(ssl_type, "SPECIFIED") */ + user.ssl_type=SSL_TYPE_SPECIFIED; + + user.ssl_cipher= get_field(&acl_memroot, user_table.ssl_cipher()); + user.x509_issuer= get_field(&acl_memroot, user_table.x509_issuer()); + user.x509_subject= get_field(&acl_memroot, user_table.x509_subject()); + + char *ptr = get_field(thd->mem_root, user_table.max_questions()); + user.user_resource.questions=ptr ? atoi(ptr) : 0; + ptr = get_field(thd->mem_root, user_table.max_updates()); + user.user_resource.updates=ptr ? atoi(ptr) : 0; + ptr = get_field(thd->mem_root, user_table.max_connections()); + user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0; + if (user.user_resource.questions || user.user_resource.updates || + user.user_resource.conn_per_hour) + mqh_used=1; + + if (user_table.max_user_connections()) { - char *ssl_type=get_field(thd->mem_root, user_table.ssl_type()); - if (!ssl_type) - user.ssl_type=SSL_TYPE_NONE; - else if (!strcmp(ssl_type, "ANY")) - user.ssl_type=SSL_TYPE_ANY; - else if (!strcmp(ssl_type, "X509")) - user.ssl_type=SSL_TYPE_X509; - else /* !strcmp(ssl_type, "SPECIFIED") */ - user.ssl_type=SSL_TYPE_SPECIFIED; - - user.ssl_cipher= get_field(&acl_memroot, user_table.ssl_cipher()); - user.x509_issuer= get_field(&acl_memroot, user_table.x509_issuer()); - user.x509_subject= get_field(&acl_memroot, user_table.x509_subject()); - - char *ptr = get_field(thd->mem_root, user_table.max_questions()); - user.user_resource.questions=ptr ? atoi(ptr) : 0; - ptr = get_field(thd->mem_root, user_table.max_updates()); - user.user_resource.updates=ptr ? atoi(ptr) : 0; - ptr = get_field(thd->mem_root, user_table.max_connections()); - user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0; - if (user.user_resource.questions || user.user_resource.updates || - user.user_resource.conn_per_hour) - mqh_used=1; - - if (user_table.max_user_connections()) - { - /* Starting from 5.0.3 we have max_user_connections field */ - ptr= get_field(thd->mem_root, user_table.max_user_connections()); - user.user_resource.user_conn= ptr ? atoi(ptr) : 0; - } + /* Starting from 5.0.3 we have max_user_connections field */ + ptr= get_field(thd->mem_root, user_table.max_user_connections()); + user.user_resource.user_conn= ptr ? atoi(ptr) : 0; + } - if (!is_role && user_table.plugin()) + if (!is_role && user_table.plugin()) + { + /* We may have plugin & auth_string fields */ + char *tmpstr= get_field(&acl_memroot, user_table.plugin()); + if (tmpstr) { - /* We may have plugin & auth_String fields */ - char *tmpstr= get_field(&acl_memroot, user_table.plugin()); - if (tmpstr) + LEX_CSTRING password= user.auth_string; + user.plugin.str= tmpstr; + user.plugin.length= strlen(user.plugin.str); + user.auth_string.str= + safe_str(get_field(&acl_memroot, + user_table.authentication_string())); + user.auth_string.length= strlen(user.auth_string.str); + + if (password.length) { - user.plugin.str= tmpstr; - user.plugin.length= strlen(user.plugin.str); - user.auth_string.str= - safe_str(get_field(&acl_memroot, - user_table.authentication_string())); - user.auth_string.length= strlen(user.auth_string.str); - - if (user.auth_string.length && password_len && - (user.auth_string.length != password_len || - memcmp(user.auth_string.str, password, password_len))) + if (user.auth_string.length && + (user.auth_string.length != password.length || + memcmp(user.auth_string.str, password.str, password.length))) { sql_print_warning("'user' entry '%s@%s' has both a password " "and an authentication plugin specified. The " "password will be ignored.", - safe_str(user.user.str), - safe_str(user.host.hostname)); + user.user.str, safe_str(user.host.hostname)); } - - fix_user_plugin_ptr(&user); + else + user.auth_string= password; } - } - if (user_table.max_statement_time()) - { - /* Starting from 10.1.1 we can have max_statement_time */ - ptr= get_field(thd->mem_root, - user_table.max_statement_time()); - user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0; + fix_user_plugin_ptr(&user); } } - else + + if (user_table.max_statement_time()) { - user.ssl_type=SSL_TYPE_NONE; + /* Starting from 10.1.1 we can have max_statement_time */ + ptr= get_field(thd->mem_root, + user_table.max_statement_time()); + user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0; + } + } + else + { + user.ssl_type=SSL_TYPE_NONE; #ifndef TO_BE_REMOVED - if (user_table.num_fields() <= 13) - { // Without grant - if (user.access & CREATE_ACL) - user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; - } - /* Convert old privileges */ - user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL; - if (user.access & FILE_ACL) - user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL; - if (user.access & PROCESS_ACL) - user.access|= SUPER_ACL | EXECUTE_ACL; -#endif + if (user_table.num_fields() <= 13) + { // Without grant + if (user.access & CREATE_ACL) + user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; } + /* Convert old privileges */ + user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL; + if (user.access & FILE_ACL) + user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL; + if (user.access & PROCESS_ACL) + user.access|= SUPER_ACL | EXECUTE_ACL; +#endif + } - (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *), - 8, 8, MYF(0)); + my_init_dynamic_array(&user.role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0)); - /* check default role, if any */ - if (!is_role && user_table.default_role()) - { - user.default_rolename.str= - get_field(&acl_memroot, user_table.default_role()); - user.default_rolename.length= safe_strlen(user.default_rolename.str); - } + /* check default role, if any */ + if (!is_role && user_table.default_role()) + { + user.default_rolename.str= + get_field(&acl_memroot, user_table.default_role()); + user.default_rolename.length= safe_strlen(user.default_rolename.str); + } - if (is_role) - { - DBUG_PRINT("info", ("Found role %s", user.user.str)); - ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot); - entry->role_grants = user.role_grants; - (void) my_init_dynamic_array(&entry->parent_grantee, - sizeof(ACL_USER_BASE *), 8, 8, MYF(0)); - my_hash_insert(&acl_roles, (uchar *)entry); + if (set_user_auth(thd, &user, NULL)) + { + thd->clear_error(); // the warning is still issued + continue; + } - continue; - } - else - { - DBUG_PRINT("info", ("Found user %s", user.user.str)); - (void) push_dynamic(&acl_users,(uchar*) &user); - } - if (!user.host.hostname || - (user.host.hostname[0] == wild_many && !user.host.hostname[1])) - allow_all_hosts=1; // Anyone can connect + if (is_role) + { + DBUG_PRINT("info", ("Found role %s", user.user.str)); + ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot); + entry->role_grants = user.role_grants; + my_init_dynamic_array(&entry->parent_grantee, + sizeof(ACL_USER_BASE *), 0, 8, MYF(0)); + my_hash_insert(&acl_roles, (uchar *)entry); + + continue; } + DBUG_PRINT("info", ("Found user %s", user.user.str)); + push_new_user(user); } my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, sizeof(ACL_USER),(qsort_cmp) acl_compare); @@ -2041,7 +1985,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) { ACL_DB db; char *db_name; - db.user=get_field(&acl_memroot, db_table.user()); + db.user=safe_str(get_field(&acl_memroot, db_table.user())); const char *hostname= get_field(&acl_memroot, db_table.host()); if (!hostname && find_acl_role(db.user)) hostname= ""; @@ -2056,7 +2000,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) { sql_print_warning("'db' entry '%s %s@%s' " "ignored in --skip-name-resolve mode.", - db.db, safe_str(db.user), safe_str(db.host.hostname)); + db.db, db.user, safe_str(db.host.hostname)); continue; } db.access= db_table.get_access(); @@ -2081,7 +2025,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) "case that has been forced to lowercase because " "lower_case_table_names is set. It will not be " "possible to remove this privilege using REVOKE.", - db.db, safe_str(db.user), safe_str(db.host.hostname)); + db.db, db.user, safe_str(db.host.hostname)); } } db.sort=get_sort(3,db.host.hostname,db.db,db.user); @@ -2409,7 +2353,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'", host, ip, user, db)); - sctx->user= user; + sctx->user= *user ? user : NULL; sctx->host= host; sctx->ip= ip; sctx->host_or_ip= host ? host : (safe_str(ip)); @@ -2439,8 +2383,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, for (i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); - if (!acl_db->user || - (user && user[0] && !strcmp(user, acl_db->user))) + if (!*acl_db->user || !strcmp(user, acl_db->user)) { if (compare_hostname(&acl_db->host, host, ip)) { @@ -2454,8 +2397,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, } sctx->master_access= acl_user->access; - if (acl_user->user.str) - strmake_buf(sctx->priv_user, user); + strmake_buf(sctx->priv_user, user); if (acl_user->host.hostname) strmake_buf(sctx->priv_host, acl_user->host.hostname); @@ -2470,8 +2412,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, for (i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); - if (!acl_db->user || - (user && user[0] && !strcmp(user, acl_db->user))) + if (!*acl_db->user || !strcmp(user, acl_db->user)) { if (compare_hostname(&acl_db->host, "", "")) { @@ -2485,8 +2426,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, } sctx->master_access= acl_role->access; - if (acl_role->user.str) - strmake_buf(sctx->priv_role, user); + strmake_buf(sctx->priv_role, user); } } @@ -2609,66 +2549,55 @@ static void acl_update_role(const char *rolename, ulong privileges) } -static void acl_update_user(const char *user, const char *host, - const char *password, size_t password_len, - enum SSL_type ssl_type, - const char *ssl_cipher, - const char *x509_issuer, - const char *x509_subject, - USER_RESOURCES *mqh, - ulong privileges, - const LEX_CSTRING *plugin, - const LEX_CSTRING *auth) +static int acl_user_update(THD *thd, ACL_USER *acl_user, const ACL_USER *from, + const LEX_USER &combo, enum SSL_type ssl_type, + const char *ssl_cipher, const char *x509_issuer, + const char *x509_subject, const USER_RESOURCES *mqh, + ulong privileges) { - mysql_mutex_assert_owner(&acl_cache->lock); + if (from) + *acl_user= *from; + else + { + bzero(acl_user, sizeof(*acl_user)); + acl_user->user= safe_lexcstrdup_root(&acl_memroot, combo.user); + update_hostname(&acl_user->host, safe_strdup_root(&acl_memroot, combo.host.str)); + acl_user->hostname_length= combo.host.length; + acl_user->sort= get_sort(2, acl_user->host.hostname, acl_user->user.str); + acl_user->plugin= native_password_plugin_name; + acl_user->salt= acl_user->auth_string= empty_clex_str; + my_init_dynamic_array(&acl_user->role_grants, sizeof(ACL_USER *), + 0, 8, MYF(0)); + } - for (uint i=0 ; i < acl_users.elements ; i++) + if (combo.plugin.length) { - ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); - if (acl_user->eq(user, host)) - { - if (plugin->str[0]) - { - acl_user->plugin= *plugin; - acl_user->auth_string.str= auth->str ? - strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>(""); - acl_user->auth_string.length= auth->length; - if (fix_user_plugin_ptr(acl_user)) - acl_user->plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length); - } - else - if (password[0]) - { - acl_user->auth_string.str= strmake_root(&acl_memroot, password, password_len); - acl_user->auth_string.length= password_len; - set_user_salt(acl_user, password, password_len); - set_user_plugin(acl_user, password_len); - } - acl_user->access=privileges; - if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) - acl_user->user_resource.questions=mqh->questions; - if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) - acl_user->user_resource.updates=mqh->updates; - if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) - acl_user->user_resource.conn_per_hour= mqh->conn_per_hour; - if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS) - acl_user->user_resource.user_conn= mqh->user_conn; - if (mqh->specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) - acl_user->user_resource.max_statement_time= mqh->max_statement_time; - if (ssl_type != SSL_TYPE_NOT_SPECIFIED) - { - acl_user->ssl_type= ssl_type; - acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : - 0); - acl_user->x509_issuer= (x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : - 0); - acl_user->x509_subject= (x509_subject ? - strdup_root(&acl_memroot,x509_subject) : 0); - } - /* search complete: */ - break; - } + acl_user->plugin= combo.plugin; + acl_user->auth_string= safe_lexcstrdup_root(&acl_memroot, combo.auth); + if (fix_user_plugin_ptr(acl_user)) + acl_user->plugin= safe_lexcstrdup_root(&acl_memroot, combo.plugin); + if (set_user_auth(thd, acl_user, &combo.pwtext)) + return 1; + } + acl_user->access= privileges; + if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) + acl_user->user_resource.questions= mqh->questions; + if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) + acl_user->user_resource.updates= mqh->updates; + if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) + acl_user->user_resource.conn_per_hour= mqh->conn_per_hour; + if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS) + acl_user->user_resource.user_conn= mqh->user_conn; + if (mqh->specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) + acl_user->user_resource.max_statement_time= mqh->max_statement_time; + if (ssl_type != SSL_TYPE_NOT_SPECIFIED) + { + acl_user->ssl_type= ssl_type; + acl_user->ssl_cipher= safe_strdup_root(&acl_memroot, ssl_cipher); + acl_user->x509_issuer= safe_strdup_root(&acl_memroot,x509_issuer); + acl_user->x509_subject= safe_strdup_root(&acl_memroot,x509_subject); } + return 0; } @@ -2678,82 +2607,14 @@ static void acl_insert_role(const char *rolename, ulong privileges) mysql_mutex_assert_owner(&acl_cache->lock); entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot); - (void) my_init_dynamic_array(&entry->parent_grantee, - sizeof(ACL_USER_BASE *), 8, 8, MYF(0)); - (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *), - 8, 8, MYF(0)); + my_init_dynamic_array(&entry->parent_grantee, + sizeof(ACL_USER_BASE *), 0, 8, MYF(0)); + my_init_dynamic_array(&entry->role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0)); my_hash_insert(&acl_roles, (uchar *)entry); } -static void acl_insert_user(const char *user, const char *host, - const char *password, size_t password_len, - enum SSL_type ssl_type, - const char *ssl_cipher, - const char *x509_issuer, - const char *x509_subject, - USER_RESOURCES *mqh, - ulong privileges, - const LEX_CSTRING *plugin, - const LEX_CSTRING *auth) -{ - ACL_USER acl_user; - - mysql_mutex_assert_owner(&acl_cache->lock); - - bzero(&acl_user, sizeof(acl_user)); - acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0; - acl_user.user.length= strlen(user); - update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host)); - if (plugin->str[0]) - { - acl_user.plugin= *plugin; - acl_user.auth_string.str= auth->str ? - strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>(""); - acl_user.auth_string.length= auth->length; - if (fix_user_plugin_ptr(&acl_user)) - acl_user.plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length); - } - else - { - acl_user.auth_string.str= strmake_root(&acl_memroot, password, password_len); - acl_user.auth_string.length= password_len; - set_user_salt(&acl_user, password, password_len); - set_user_plugin(&acl_user, password_len); - } - - acl_user.flags= 0; - acl_user.access=privileges; - acl_user.user_resource = *mqh; - acl_user.sort=get_sort(2, acl_user.host.hostname, acl_user.user.str); - acl_user.hostname_length=(uint) strlen(host); - acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ? - ssl_type : SSL_TYPE_NONE); - acl_user.ssl_cipher= ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : 0; - acl_user.x509_issuer= x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : 0; - acl_user.x509_subject=x509_subject ? strdup_root(&acl_memroot,x509_subject) : 0; - (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *), - 8, 8, MYF(0)); - - (void) push_dynamic(&acl_users,(uchar*) &acl_user); - if (!acl_user.host.hostname || - (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1])) - allow_all_hosts=1; // Anyone can connect /* purecov: tested */ - my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, - sizeof(ACL_USER),(qsort_cmp) acl_compare); - - /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ - rebuild_check_host(); - - /* - Rebuild every user's role_grants since 'acl_users' has been sorted - and old pointers to ACL_USER elements are no longer valid - */ - rebuild_role_grants(); -} - - static bool acl_update_db(const char *user, const char *host, const char *db, ulong privileges) { @@ -2764,13 +2625,10 @@ static bool acl_update_db(const char *user, const char *host, const char *db, for (uint i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*); - if ((!acl_db->user && !user[0]) || - (acl_db->user && - !strcmp(user,acl_db->user))) + if (!strcmp(user, acl_db->user)) { if ((!acl_db->host.hostname && !host[0]) || - (acl_db->host.hostname && - !strcmp(host, acl_db->host.hostname))) + (acl_db->host.hostname && !strcmp(host, acl_db->host.hostname))) { if ((!acl_db->db && !db[0]) || (acl_db->db && !strcmp(db,acl_db->db))) @@ -2868,7 +2726,7 @@ ulong acl_get(const char *host, const char *ip, for (i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*); - if (!acl_db->user || !strcmp(user,acl_db->user)) + if (!*acl_db->user || !strcmp(user, acl_db->user)) { if (compare_hostname(&acl_db->host,host,ip)) { @@ -3225,8 +3083,7 @@ bool check_change_password(THD *thd, LEX_USER *user) { LEX_USER *real_user= get_current_user(thd, user); - if (fix_and_copy_user(real_user, user, thd) || - validate_password(real_user, thd)) + if (fix_and_copy_user(real_user, user, thd)) return true; *user= *real_user; @@ -3256,8 +3113,8 @@ bool change_password(THD *thd, LEX_USER *user) const CSET_STRING query_save __attribute__((unused)) = thd->query_string; DBUG_ENTER("change_password"); DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'", - user->host.str, user->user.str, user->pwhash.str)); - DBUG_ASSERT(user->host.str != 0); // Ensured by parent + user->host.str, user->user.str, user->auth.str)); + DBUG_ASSERT(user->host.str != 0); // Ensured by caller /* This statement will be replicated as a statement, even when using @@ -3268,19 +3125,8 @@ bool change_password(THD *thd, LEX_USER *user) */ save_binlog_format= thd->set_current_stmt_binlog_format_stmt(); - if (mysql_bin_log.is_open() || - (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))) - { - query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'", - safe_str(user->user.str), safe_str(user->host.str), - safe_str(user->pwhash.str)); - } - if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) - { - thd->set_query(buff, query_length, system_charset_info); WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL); - } if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); @@ -3297,25 +3143,21 @@ bool change_password(THD *thd, LEX_USER *user) goto end; } - /* update loaded acl entry: */ if (acl_user->plugin.str == native_password_plugin_name.str || acl_user->plugin.str == old_password_plugin_name.str) { - acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length); - acl_user->auth_string.length= user->pwhash.length; - set_user_salt(acl_user, user->pwhash.str, user->pwhash.length); + /* historical hack of auto-changing the plugin */ + acl_user->plugin= guess_auth_plugin(thd, user->auth.length); + } - set_user_plugin(acl_user, user->pwhash.length); + acl_user->auth_string= safe_lexcstrdup_root(&acl_memroot, user->auth); + if (set_user_auth(thd, acl_user, &user->pwtext)) + { + mysql_mutex_unlock(&acl_cache->lock); + goto end; } - else - push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_SET_PASSWORD_AUTH_PLUGIN, - ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN)); - if (update_user_table(thd, tables.user_table(), - safe_str(acl_user->host.hostname), - safe_str(acl_user->user.str), - user->pwhash.str, user->pwhash.length)) + if (update_user_table_password(thd, tables.user_table(), *acl_user)) { mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */ goto end; @@ -3326,6 +3168,8 @@ bool change_password(THD *thd, LEX_USER *user) result= 0; if (mysql_bin_log.is_open()) { + query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'", + user->user.str, safe_str(user->host.str), acl_user->auth_string.str); DBUG_ASSERT(query_length); thd->clear_error(); result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length, @@ -3335,7 +3179,7 @@ end: close_mysql_tables(thd); #ifdef WITH_WSREP -WSREP_ERROR_LABEL: +wsrep_error_label: if (WSREP(thd) && !thd->wsrep_applier) { WSREP_TO_ISOLATION_END; @@ -3364,13 +3208,12 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, ulong query_length= 0; bool clear_role= FALSE; char buff[512]; - enum_binlog_format save_binlog_format= - thd->get_current_stmt_binlog_format(); + enum_binlog_format save_binlog_format= thd->get_current_stmt_binlog_format(); const CSET_STRING query_save __attribute__((unused)) = thd->query_string; DBUG_ENTER("acl_set_default_role"); DBUG_PRINT("enter",("host: '%s' user: '%s' rolename: '%s'", - safe_str(user), safe_str(host), safe_str(rolename))); + user, safe_str(host), safe_str(rolename))); if (rolename == current_role.str) { if (!thd->security_ctx->priv_role[0]) @@ -3390,7 +3233,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, { query_length= sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'", - safe_str(rolename), safe_str(user), safe_str(host)); + safe_str(rolename), user, safe_str(host)); } /* @@ -3498,7 +3341,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, } #ifdef WITH_WSREP -WSREP_ERROR_LABEL: +wsrep_error_label: if (WSREP(thd) && !thd->wsrep_applier) { WSREP_TO_ISOLATION_END; @@ -3559,7 +3402,7 @@ static ACL_USER *find_user_or_anon(const char *host, const char *user, const cha for (uint i=0; i < acl_users.elements; i++) { ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*); - if ((!acl_user_tmp->user.str || + if ((!acl_user_tmp->user.length || !strcmp(user, acl_user_tmp->user.str)) && compare_hostname(&acl_user_tmp->host, host, ip)) { @@ -3574,13 +3417,13 @@ static ACL_USER *find_user_or_anon(const char *host, const char *user, const cha /* Find first entry that matches the specified user@host pair */ -static ACL_USER * find_user_exact(const char *host, const char *user) +static ACL_USER *find_user_exact(const char *host, const char *user) { mysql_mutex_assert_owner(&acl_cache->lock); for (uint i=0 ; i < acl_users.elements ; i++) { - ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); + ACL_USER *acl_user=dynamic_element(&acl_users, i, ACL_USER*); if (acl_user->eq(user, host)) return acl_user; } @@ -3615,7 +3458,7 @@ static ACL_ROLE *find_acl_role(const char *role) mysql_mutex_assert_owner(&acl_cache->lock); ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role, - safe_strlen(role)); + strlen(role)); DBUG_RETURN(r); } @@ -3767,53 +3610,24 @@ bool hostname_requires_resolving(const char *hostname) } -void set_authentication_plugin_from_password(const User_table& user_table, - const char* password, size_t password_length) -{ - if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH || - password_length == 0) - { - user_table.plugin()->store(native_password_plugin_name.str, - native_password_plugin_name.length, - system_charset_info); - } - else - { - DBUG_ASSERT(password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323); - user_table.plugin()->store(old_password_plugin_name.str, - old_password_plugin_name.length, - system_charset_info); - } - user_table.authentication_string()->store(password, - password_length, - system_charset_info); -} /** Update record for user in mysql.user privilege table with new password. - @param thd THD - @param table Pointer to TABLE object for open mysql.user table - @param host Hostname - @param user Username - @param new_password New password hash - @param new_password_len Length of new password hash - @see change_password */ -static bool update_user_table(THD *thd, const User_table& user_table, - const char *host, const char *user, - const char *new_password, size_t new_password_len) +static bool update_user_table_password(THD *thd, const User_table& user_table, + const ACL_USER &user) { + CHARSET_INFO *cs= system_charset_info; char user_key[MAX_KEY_LENGTH]; int error; - DBUG_ENTER("update_user_table"); - DBUG_PRINT("enter",("user: %s host: %s",user,host)); + DBUG_ENTER("update_user_table_password"); TABLE *table= user_table.table(); table->use_all_columns(); - user_table.host()->store(host,(uint) strlen(host), system_charset_info); - user_table.user()->store(user,(uint) strlen(user), system_charset_info); + user_table.host()->store(user.host.hostname, user.hostname_length, cs); + user_table.user()->store(user.user.str, user.user.length, cs); key_copy((uchar *) user_key, table->record[0], table->key_info, table->key_info->key_length); @@ -3829,13 +3643,13 @@ static bool update_user_table(THD *thd, const User_table& user_table, if (user_table.plugin()) { - set_authentication_plugin_from_password(user_table, new_password, - new_password_len); + if (user_table.password()) + user_table.password()->reset(); + user_table.plugin()->store(user.plugin.str, user.plugin.length, cs); + user_table.authentication_string()->store(user.auth_string.str, user.auth_string.length, cs); } - - if (user_table.password()) - user_table.password()->store(new_password, new_password_len, system_charset_info); - + else + user_table.password()->store(user.auth_string.str, user.auth_string.length, cs); if (unlikely(error= table->file->ha_update_row(table->record[1], table->record[0])) && @@ -3887,7 +3701,7 @@ static bool test_if_create_new_users(THD *thd) ****************************************************************************/ static int replace_user_table(THD *thd, const User_table &user_table, - LEX_USER &combo, + LEX_USER *combo, ulong rights, bool revoke_grant, bool can_create_user, bool no_auto_create) { @@ -3895,26 +3709,14 @@ static int replace_user_table(THD *thd, const User_table &user_table, bool old_row_exists=0; char what= (revoke_grant) ? 'N' : 'Y'; uchar user_key[MAX_KEY_LENGTH]; - bool handle_as_role= combo.is_role(); + bool handle_as_role= combo->is_role(); LEX *lex= thd->lex; TABLE *table= user_table.table(); + ACL_USER new_acl_user, *old_acl_user; DBUG_ENTER("replace_user_table"); mysql_mutex_assert_owner(&acl_cache->lock); - if (combo.pwhash.str && combo.pwhash.str[0]) - { - if (combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH && - combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323) - { - DBUG_ASSERT(0); - my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH); - DBUG_RETURN(-1); - } - } - else - combo.pwhash= empty_clex_str; - /* if the user table is not up to date, we can't handle role updates */ if (!user_table.is_role() && handle_as_role) { @@ -3925,9 +3727,9 @@ static int replace_user_table(THD *thd, const User_table &user_table, } table->use_all_columns(); - user_table.host()->store(combo.host.str,combo.host.length, + user_table.host()->store(combo->host.str,combo->host.length, system_charset_info); - user_table.user()->store(combo.user.str,combo.user.length, + user_table.user()->store(combo->user.str,combo->user.length, system_charset_info); key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); @@ -3939,7 +3741,7 @@ static int replace_user_table(THD *thd, const User_table &user_table, /* what == 'N' means revoke */ if (what == 'N') { - my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str); + my_error(ER_NONEXISTING_GRANT, MYF(0), combo->user.str, combo->host.str); goto end; } /* @@ -3955,7 +3757,8 @@ static int replace_user_table(THD *thd, const User_table &user_table, see also test_if_create_new_users() */ - else if (!combo.pwhash.length && !combo.plugin.length && no_auto_create) + else if (!combo->auth.length && !combo->plugin.length && + !combo->pwtext.length && no_auto_create) { my_error(ER_PASSWORD_NO_MATCH, MYF(0)); goto end; @@ -3965,32 +3768,37 @@ static int replace_user_table(THD *thd, const User_table &user_table, my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0)); goto end; } - else if (combo.plugin.str[0]) + else if (combo->plugin.length) { - if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN)) + if (!plugin_is_ready(&combo->plugin, MYSQL_AUTHENTICATION_PLUGIN)) { - my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str); + my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo->plugin.str); goto end; } } + else /* combo->plugin.length == 0 */ + { + combo->plugin= guess_auth_plugin(thd, combo->auth.length); + } old_row_exists = 0; restore_record(table,s->default_values); - user_table.host()->store(combo.host.str,combo.host.length, + user_table.host()->store(combo->host.str,combo->host.length, system_charset_info); - user_table.user()->store(combo.user.str,combo.user.length, + user_table.user()->store(combo->user.str,combo->user.length, system_charset_info); } else { old_row_exists = 1; store_record(table,record[1]); // Save copy for update + if (!combo->plugin.length && (combo->auth.length || combo->pwtext.length)) + { + /* GRANT ... IDENTIFIED BY */ + combo->plugin= guess_auth_plugin(thd, combo->auth.length); + } } - if (!old_row_exists || combo.pwtext.length || combo.pwhash.length) - if (!handle_as_role && validate_password(&combo, thd)) - goto end; - /* Update table columns with new privileges */ ulong priv; @@ -4003,122 +3811,112 @@ static int replace_user_table(THD *thd, const User_table &user_table, rights= user_table.get_access(); - DBUG_PRINT("info",("table fields: %d", user_table.num_fields())); - /* If we don't have a password column, we'll use the authentication_string - column later. */ - if (combo.pwhash.str[0] && user_table.password()) - user_table.password()->store(combo.pwhash.str, combo.pwhash.length, - system_charset_info); - /* We either have the password column, the plugin column, or both. Otherwise - we have a corrupt user table. */ - DBUG_ASSERT(user_table.password() || user_table.plugin()); - if (user_table.ssl_type()) /* From 4.0.0 we have more fields */ - { - /* We write down SSL related ACL stuff */ - switch (lex->ssl_type) { - case SSL_TYPE_ANY: - user_table.ssl_type()->store(STRING_WITH_LEN("ANY"), - &my_charset_latin1); - user_table.ssl_cipher()->store("", 0, &my_charset_latin1); - user_table.x509_issuer()->store("", 0, &my_charset_latin1); - user_table.x509_subject()->store("", 0, &my_charset_latin1); - break; - case SSL_TYPE_X509: - user_table.ssl_type()->store(STRING_WITH_LEN("X509"), - &my_charset_latin1); - user_table.ssl_cipher()->store("", 0, &my_charset_latin1); - user_table.x509_issuer()->store("", 0, &my_charset_latin1); - user_table.x509_subject()->store("", 0, &my_charset_latin1); - break; - case SSL_TYPE_SPECIFIED: - user_table.ssl_type()->store(STRING_WITH_LEN("SPECIFIED"), - &my_charset_latin1); - user_table.ssl_cipher()->store("", 0, &my_charset_latin1); - user_table.x509_issuer()->store("", 0, &my_charset_latin1); - user_table.x509_subject()->store("", 0, &my_charset_latin1); - if (lex->ssl_cipher) - user_table.ssl_cipher()->store(lex->ssl_cipher, - strlen(lex->ssl_cipher), - system_charset_info); - if (lex->x509_issuer) - user_table.x509_issuer()->store(lex->x509_issuer, - strlen(lex->x509_issuer), - system_charset_info); - if (lex->x509_subject) - user_table.x509_subject()->store(lex->x509_subject, - strlen(lex->x509_subject), - system_charset_info); - break; - case SSL_TYPE_NOT_SPECIFIED: - break; - case SSL_TYPE_NONE: - user_table.ssl_type()->store("", 0, &my_charset_latin1); - user_table.ssl_cipher()->store("", 0, &my_charset_latin1); - user_table.x509_issuer()->store("", 0, &my_charset_latin1); - user_table.x509_subject()->store("", 0, &my_charset_latin1); - break; + if (handle_as_role) + { + if (old_row_exists && !user_table.check_is_role()) + { + goto end; + } + user_table.is_role()->store("Y", 1, system_charset_info); + } + else + { + old_acl_user= find_user_exact(combo->host.str, combo->user.str); + if ((old_acl_user != NULL) != old_row_exists) + { + my_error(ER_PASSWORD_NO_MATCH, MYF(0)); + goto end; } + if (acl_user_update(thd, &new_acl_user, + old_row_exists ? old_acl_user : NULL, + *combo, lex->ssl_type, lex->ssl_cipher, + lex->x509_issuer, lex->x509_subject, &lex->mqh, + rights)) + goto end; - USER_RESOURCES mqh= lex->mqh; - if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) - user_table.max_questions()->store((longlong) mqh.questions, TRUE); - if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) - user_table.max_updates()->store((longlong) mqh.updates, TRUE); - if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) - user_table.max_connections()->store((longlong) mqh.conn_per_hour, TRUE); - if (user_table.max_user_connections() && - (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) - user_table.max_user_connections()->store((longlong) mqh.user_conn, FALSE); - if (user_table.plugin()) - { - user_table.plugin()->set_notnull(); - user_table.authentication_string()->set_notnull(); - if (combo.plugin.str[0]) - { - DBUG_ASSERT(combo.pwhash.str[0] == 0); - if (user_table.password()) - user_table.password()->reset(); - user_table.plugin()->store(combo.plugin.str, combo.plugin.length, - system_charset_info); - user_table.authentication_string()->store(combo.auth.str, combo.auth.length, - system_charset_info); + DBUG_PRINT("info",("table fields: %d", user_table.num_fields())); + /* We either have the password column, the plugin column, or both. Otherwise + we have a corrupt user table. */ + DBUG_ASSERT(user_table.password() || user_table.plugin()); + if (user_table.ssl_type()) /* From 4.0.0 we have more fields */ + { + /* We write down SSL related ACL stuff */ + switch (lex->ssl_type) { + case SSL_TYPE_ANY: + user_table.ssl_type()->store(STRING_WITH_LEN("ANY"), + &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); + break; + case SSL_TYPE_X509: + user_table.ssl_type()->store(STRING_WITH_LEN("X509"), + &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); + break; + case SSL_TYPE_SPECIFIED: + user_table.ssl_type()->store(STRING_WITH_LEN("SPECIFIED"), + &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); + if (lex->ssl_cipher) + user_table.ssl_cipher()->store(lex->ssl_cipher, + strlen(lex->ssl_cipher), + system_charset_info); + if (lex->x509_issuer) + user_table.x509_issuer()->store(lex->x509_issuer, + strlen(lex->x509_issuer), + system_charset_info); + if (lex->x509_subject) + user_table.x509_subject()->store(lex->x509_subject, + strlen(lex->x509_subject), + system_charset_info); + break; + case SSL_TYPE_NOT_SPECIFIED: + break; + case SSL_TYPE_NONE: + user_table.ssl_type()->store("", 0, &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); + break; } - if (combo.pwhash.str[0]) + + USER_RESOURCES mqh= lex->mqh; + if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) + user_table.max_questions()->store((longlong) mqh.questions, TRUE); + if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) + user_table.max_updates()->store((longlong) mqh.updates, TRUE); + if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) + user_table.max_connections()->store((longlong) mqh.conn_per_hour, TRUE); + if (user_table.max_user_connections() && + (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) + user_table.max_user_connections()->store((longlong) mqh.user_conn, FALSE); + if (user_table.plugin()) { - DBUG_ASSERT(combo.plugin.str[0] == 0); - /* We have Password column. */ - if (user_table.password()) + user_table.plugin()->set_notnull(); + user_table.authentication_string()->set_notnull(); + if (new_acl_user.plugin.length) { - user_table.plugin()->reset(); - user_table.authentication_string()->reset(); + if (user_table.password()) + user_table.password()->reset(); + user_table.plugin()->store(new_acl_user.plugin.str, + new_acl_user.plugin.length, system_charset_info); + user_table.authentication_string()->store(new_acl_user.auth_string.str, + new_acl_user.auth_string.length, system_charset_info); } - else + + if (user_table.max_statement_time()) { - /* We do not have Password column. Use PLUGIN && Authentication_string - columns instead. */ - set_authentication_plugin_from_password(user_table, - combo.pwhash.str, - combo.pwhash.length); + if (mqh.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) + user_table.max_statement_time()->store(mqh.max_statement_time); } } - - if (user_table.max_statement_time()) - { - if (mqh.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) - user_table.max_statement_time()->store(mqh.max_statement_time); - } - } - mqh_used= (mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour || - mqh.user_conn || mqh.max_statement_time != 0.0); - - /* table format checked earlier */ - if (handle_as_role) - { - if (old_row_exists && !user_table.check_is_role()) - { - goto end; - } - user_table.is_role()->store("Y", 1, system_charset_info); + mqh_used= (mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour || + mqh.user_conn || mqh.max_statement_time != 0.0); } } @@ -4158,37 +3956,32 @@ end: if (likely(!error)) { acl_cache->clear(1); // Clear privilege cache - if (old_row_exists) + if (handle_as_role) { - if (handle_as_role) - acl_update_role(combo.user.str, rights); + if (old_row_exists) + acl_update_role(combo->user.str, rights); else - acl_update_user(combo.user.str, combo.host.str, - combo.pwhash.str, combo.pwhash.length, - lex->ssl_type, - lex->ssl_cipher, - lex->x509_issuer, - lex->x509_subject, - &lex->mqh, - rights, - &combo.plugin, - &combo.auth); + acl_insert_role(combo->user.str, rights); } else { - if (handle_as_role) - acl_insert_role(combo.user.str, rights); + if (old_acl_user) + *old_acl_user= new_acl_user; else - acl_insert_user(combo.user.str, combo.host.str, - combo.pwhash.str, combo.pwhash.length, - lex->ssl_type, - lex->ssl_cipher, - lex->x509_issuer, - lex->x509_subject, - &lex->mqh, - rights, - &combo.plugin, - &combo.auth); + { + push_new_user(new_acl_user); + my_qsort(dynamic_element(&acl_users, 0, ACL_USER*), acl_users.elements, + sizeof(ACL_USER),(qsort_cmp) acl_compare); + + /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ + rebuild_check_host(); + + /* + Rebuild every user's role_grants since 'acl_users' has been sorted + and old pointers to ACL_USER elements are no longer valid + */ + rebuild_role_grants(); + } } } DBUG_RETURN(error); @@ -6291,7 +6084,7 @@ static bool merge_one_role_privileges(ACL_ROLE *grantee) static bool has_auth(LEX_USER *user, LEX *lex) { - return user->pwtext.length || user->pwhash.length || user->plugin.length || user->auth.length || + return user->pwtext.length || user->plugin.length || user->auth.length || lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher || lex->x509_issuer || lex->x509_subject || lex->mqh.specified_limits; @@ -6303,14 +6096,9 @@ static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd) { /* preserve authentication information, if LEX_USER was reallocated */ to->pwtext= from->pwtext; - to->pwhash= from->pwhash; to->plugin= from->plugin; to->auth= from->auth; } - - if (fix_lex_user(thd, to)) - return true; - return false; } @@ -6474,7 +6262,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, } /* Create user if needed */ error= copy_and_check_auth(Str, tmp_Str, thd) || - replace_user_table(thd, tables.user_table(), *Str, + replace_user_table(thd, tables.user_table(), Str, 0, revoke_grant, create_new_users, MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER)); @@ -6653,7 +6441,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, } /* Create user if needed */ if (copy_and_check_auth(Str, tmp_Str, thd) || - replace_user_table(thd, tables.user_table(), *Str, + replace_user_table(thd, tables.user_table(), Str, 0, revoke_grant, create_new_users, MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER))) @@ -6929,7 +6717,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) user_combo.user = username; if (copy_and_check_auth(&user_combo, &user_combo, thd) || - replace_user_table(thd, tables.user_table(), user_combo, 0, + replace_user_table(thd, tables.user_table(), &user_combo, 0, false, create_new_user, no_auto_create_user)) { @@ -7099,7 +6887,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, } if (copy_and_check_auth(Str, tmp_Str, thd) || - replace_user_table(thd, tables.user_table(), *Str, + replace_user_table(thd, tables.user_table(), Str, (!db ? rights : 0), revoke_grant, create_new_users, MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER))) @@ -7263,8 +7051,7 @@ static bool grant_load(THD *thd, { sql_print_warning("'tables_priv' entry '%s %s@%s' " "ignored in --skip-name-resolve mode.", - mem_check->tname, - safe_str(mem_check->user), + mem_check->tname, mem_check->user, safe_str(mem_check->host.hostname)); continue; } @@ -8349,7 +8136,6 @@ static void add_user_parameters(String *result, ACL_USER* acl_user, { if (acl_user->auth_string.length) { - DBUG_ASSERT(acl_user->salt_len); result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); result->append(&acl_user->auth_string); result->append('\''); @@ -8869,7 +8655,7 @@ static bool show_database_privileges(THD *thd, const char *username, const char *user, *host; acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); - user= safe_str(acl_db->user); + user= acl_db->user; host=acl_db->host.hostname; /* @@ -8955,7 +8741,7 @@ static bool show_table_and_column_privileges(THD *thd, const char *username, GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, index); - user= safe_str(grant_table->user); + user= grant_table->user; host= grant_table->host.hostname; /* @@ -9097,7 +8883,7 @@ static int show_routine_grants(THD* thd, const char *user, *host; GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index); - user= safe_str(grant_proc->user); + user= grant_proc->user; host= grant_proc->host.hostname; /* @@ -9609,8 +9395,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, my_hash_delete(&acl_roles, (uchar*) acl_role); DBUG_RETURN(1); } - acl_role->user.str= strdup_root(&acl_memroot, user_to->user.str); - acl_role->user.length= user_to->user.length; + acl_role->user= safe_lexcstrdup_root(&acl_memroot, user_to->user); my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key, old_key_length); @@ -9706,8 +9491,6 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, default: DBUG_ASSERT(0); } - if (! user) - user= ""; if (! host) host= ""; @@ -9801,8 +9584,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, { switch ( struct_no ) { case USER_ACL: - acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str); - acl_user->user.length= user_to->user.length; + acl_user->user= safe_lexcstrdup_root(&acl_memroot, user_to->user); update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str)); acl_user->hostname_length= strlen(acl_user->host.hostname); break; @@ -10185,13 +9967,6 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) if (!user_name->host.str) user_name->host= host_not_specified; - if (fix_lex_user(thd, user_name)) - { - append_user(thd, &wrong_users, user_name); - result= TRUE; - continue; - } - /* Search all in-memory structures and grant tables for a mention of the new user/role name. @@ -10234,7 +10009,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) } } - if (replace_user_table(thd, tables.user_table(), *user_name, 0, 0, 1, 0)) + if (replace_user_table(thd, tables.user_table(), user_name, 0, 0, 1, 0)) { append_user(thd, &wrong_users, user_name); result= TRUE; @@ -10523,10 +10298,8 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list) while ((tmp_lex_user= users_list_iterator++)) { LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false); - if (!lex_user || - fix_lex_user(thd, lex_user) || - replace_user_table(thd, tables.user_table(), *lex_user, 0, - false, false, true)) + if (!lex_user || replace_user_table(thd, tables.user_table(), lex_user, 0, + false, false, true)) { thd->clear_error(); append_user(thd, &wrong_users, tmp_lex_user); @@ -10575,7 +10348,7 @@ mysql_revoke_sp_privs(THD *thd, { const char *user,*host; GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); - user= safe_str(grant_proc->user); + user= grant_proc->user; host= safe_str(grant_proc->host.hostname); if (!strcmp(lex_user->user.str, user) && @@ -10650,7 +10423,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) continue; } - if (replace_user_table(thd, tables.user_table(), *lex_user, + if (replace_user_table(thd, tables.user_table(), lex_user, ~(ulong)0, 1, 0, 0)) { result= -1; @@ -10671,7 +10444,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); - user= safe_str(acl_db->user); + user= acl_db->user; host= safe_str(acl_db->host.hostname); if (!strcmp(lex_user->user.str, user) && @@ -10703,7 +10476,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) const char *user,*host; GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter); - user= safe_str(grant_table->user); + user= grant_table->user; host= safe_str(grant_table->host.hostname); if (!strcmp(lex_user->user.str,user) && @@ -11313,7 +11086,7 @@ bool check_role_is_granted(const char *username, ACL_USER_BASE *root; mysql_mutex_lock(&acl_cache->lock); if (hostname) - root= find_user_exact(username, hostname); + root= find_user_exact(hostname, username); else root= find_acl_role(username); @@ -11487,7 +11260,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) { const char *user,*host, *is_grantable="YES"; acl_user=dynamic_element(&acl_users,counter,ACL_USER*); - user= safe_str(acl_user->user.str); + user= acl_user->user.str; host= safe_str(acl_user->host.hostname); if (no_global_access && @@ -11561,7 +11334,7 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond) const char *user, *host, *is_grantable="YES"; acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); - user= safe_str(acl_db->user); + user= acl_db->user; host= safe_str(acl_db->host.hostname); if (no_global_access && @@ -11633,7 +11406,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) const char *user, *host, *is_grantable= "YES"; GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, index); - user= safe_str(grant_table->user); + user= grant_table->user; host= safe_str(grant_table->host.hostname); if (no_global_access && @@ -11715,7 +11488,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) const char *user, *host, *is_grantable= "YES"; GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, index); - user= safe_str(grant_table->user); + user= grant_table->user; host= safe_str(grant_table->host.hostname); if (no_global_access && @@ -12299,8 +12072,9 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, user account, it's the plugin that the client need to use to login. */ bool switch_from_long_to_short_scramble= - native_password_plugin_name.str == mpvio->cached_client_reply.plugin && - client_auth_plugin == old_password_plugin_name.str; + client_auth_plugin == old_password_plugin_name.str && + my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin, + native_password_plugin_name.str) == 0; if (switch_from_long_to_short_scramble) DBUG_RETURN (secure_auth(mpvio->auth_info.thd) || @@ -12313,8 +12087,9 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, ask an old 4.0 client to use the new 4.1 authentication protocol. */ bool switch_from_short_to_long_scramble= - old_password_plugin_name.str == mpvio->cached_client_reply.plugin && - client_auth_plugin == native_password_plugin_name.str; + client_auth_plugin == native_password_plugin_name.str && + my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin, + old_password_plugin_name.str) == 0; if (switch_from_short_to_long_scramble) { @@ -12404,9 +12179,9 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) mpvio->auth_info.user_name= sctx->user; mpvio->auth_info.user_name_length= (uint)strlen(sctx->user); - mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str; - mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length; - strmake_buf(mpvio->auth_info.authenticated_as, safe_str(mpvio->acl_user->user.str)); + mpvio->auth_info.auth_string= mpvio->acl_user->salt.str; + mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->salt.length; + strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user.str); DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s" "plugin=%s", @@ -12557,7 +12332,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) MYF(0)); DBUG_RETURN(1); } - client_plugin= fix_plugin_ptr(next_field); + client_plugin= next_field; next_field+= strlen(next_field) + 1; } else @@ -12802,7 +12577,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) && (client_plugin < (char *)net->read_pos + pkt_len)) { - client_plugin= fix_plugin_ptr(client_plugin); next_field+= strlen(next_field) + 1; } else @@ -13141,17 +12915,7 @@ static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name, { int res= CR_OK, old_status= MPVIO_EXT::FAILURE; bool unlock_plugin= false; - plugin_ref plugin= NULL; - - if (auth_plugin_name->str == native_password_plugin_name.str) - plugin= native_password_plugin; -#ifndef EMBEDDED_LIBRARY - else if (auth_plugin_name->str == old_password_plugin_name.str) - plugin= old_password_plugin; - else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name, - MYSQL_AUTHENTICATION_PLUGIN))) - unlock_plugin= true; -#endif + plugin_ref plugin= get_auth_plugin(thd, *auth_plugin_name, &unlock_plugin); mpvio->plugin= plugin; old_status= mpvio->status; @@ -13339,7 +13103,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) { #ifndef NO_EMBEDDED_ACCESS_CHECKS bool is_proxy_user= FALSE; - const char *auth_user = safe_str(acl_user->user.str); + const char *auth_user = acl_user->user.str; ACL_PROXY_USER *proxy_user; /* check if the user is allowed to proxy as another user */ proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip, @@ -13385,10 +13149,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) #endif sctx->master_access= acl_user->access; - if (acl_user->user.str) - strmake_buf(sctx->priv_user, acl_user->user.str); - else - *sctx->priv_user= 0; + strmake_buf(sctx->priv_user, acl_user->user.str); if (acl_user->host.hostname) strmake_buf(sctx->priv_host, acl_user->host.hostname); @@ -13600,15 +13361,16 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio, DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; }); if (pkt_len == 0) /* no password */ - DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK); + DBUG_RETURN(info->auth_string_length != 0 + ? CR_AUTH_USER_CREDENTIALS : CR_OK); info->password_used= PASSWORD_USED_YES; if (pkt_len == SCRAMBLE_LENGTH) { - if (!mpvio->acl_user->salt_len) + if (!info->auth_string_length) DBUG_RETURN(CR_AUTH_USER_CREDENTIALS); - if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt)) + if (check_scramble(pkt, thd->scramble, (uchar*)info->auth_string)) DBUG_RETURN(CR_AUTH_USER_CREDENTIALS); else DBUG_RETURN(CR_OK); @@ -13618,6 +13380,41 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio, DBUG_RETURN(CR_AUTH_HANDSHAKE); } +static int native_password_make_scramble(const char *password, + size_t password_length, char *hash, size_t *hash_length) +{ + DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH); + if (password_length == 0) + *hash_length= 0; + else + { + *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; + my_make_scrambled_password(hash, password, password_length); + } + return 0; +} + +static int native_password_get_salt(const char *hash, size_t hash_length, + unsigned char *out, size_t *out_length) +{ + DBUG_ASSERT(*out_length >= SCRAMBLE_LENGTH); + if (hash_length == 0) + { + *out_length= 0; + return 0; + } + + if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH) + { + my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH); + return 1; + } + + *out_length= SCRAMBLE_LENGTH; + get_salt_from_password(out, hash); + return 0; +} + static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { @@ -13661,30 +13458,64 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, if (pkt_len == SCRAMBLE_LENGTH_323) { - if (!mpvio->acl_user->salt_len) + if (!info->auth_string_length) return CR_AUTH_USER_CREDENTIALS; - return check_scramble_323(pkt, thd->scramble, - (ulong *) mpvio->acl_user->salt) ? - CR_AUTH_USER_CREDENTIALS : CR_OK; + return check_scramble_323(pkt, thd->scramble, (ulong *) info->auth_string) + ? CR_AUTH_USER_CREDENTIALS : CR_OK; } my_error(ER_HANDSHAKE_ERROR, MYF(0)); return CR_AUTH_HANDSHAKE; } +static int old_password_make_scramble(const char *password, + size_t password_length, char *hash, size_t *hash_length) +{ + DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH_323); + if (password_length == 0) + *hash_length= 0; + else + { + *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + my_make_scrambled_password_323(hash, password, password_length); + } + return 0; +} + +#define SALT_LENGTH_323 (sizeof(ulong)*2) +static int old_password_get_salt(const char *hash, size_t hash_length, + unsigned char *out, size_t *out_length) +{ + DBUG_ASSERT(*out_length >= SALT_LENGTH_323); + + if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + { + my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH_323); + return 1; + } + + *out_length= SALT_LENGTH_323; + get_salt_from_password_323((ulong*)out, hash); + return 0; +} + static struct st_mysql_auth native_password_handler= { MYSQL_AUTHENTICATION_INTERFACE_VERSION, native_password_plugin_name.str, - native_password_authenticate + native_password_authenticate, + native_password_make_scramble, + native_password_get_salt }; static struct st_mysql_auth old_password_handler= { MYSQL_AUTHENTICATION_INTERFACE_VERSION, old_password_plugin_name.str, - old_password_authenticate + old_password_authenticate, + old_password_make_scramble, + old_password_get_salt }; maria_declare_plugin(mysql_password) diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index c17567e6a89..d0d959de8f9 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -306,7 +306,7 @@ static bool open_only_one_table(THD* thd, TABLE_LIST* table, bool is_view_operator_func) { LEX *lex= thd->lex; - SELECT_LEX *select= &lex->select_lex; + SELECT_LEX *select= lex->first_select_lex(); TABLE_LIST *save_next_global, *save_next_local; bool open_error; save_next_global= table->next_global; @@ -1301,7 +1301,7 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables) bool Sql_cmd_analyze_table::execute(THD *thd) { LEX *m_lex= thd->lex; - TABLE_LIST *first_table= m_lex->select_lex.table_list.first; + TABLE_LIST *first_table= m_lex->first_select_lex()->table_list.first; bool res= TRUE; thr_lock_type lock_type = TL_READ_NO_INSERT; DBUG_ENTER("Sql_cmd_analyze_table::execute"); @@ -1321,11 +1321,13 @@ bool Sql_cmd_analyze_table::execute(THD *thd) */ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); } - m_lex->select_lex.table_list.first= first_table; + m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; error: -WSREP_ERROR_LABEL: +#ifdef WITH_WSREP +wsrep_error_label: +#endif DBUG_RETURN(res); } @@ -1333,7 +1335,7 @@ WSREP_ERROR_LABEL: bool Sql_cmd_check_table::execute(THD *thd) { LEX *m_lex= thd->lex; - TABLE_LIST *first_table= m_lex->select_lex.table_list.first; + TABLE_LIST *first_table= m_lex->first_select_lex()->table_list.first; thr_lock_type lock_type = TL_READ_NO_INSERT; bool res= TRUE; DBUG_ENTER("Sql_cmd_check_table::execute"); @@ -1346,7 +1348,7 @@ bool Sql_cmd_check_table::execute(THD *thd) lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0, &handler::ha_check, &view_check); - m_lex->select_lex.table_list.first= first_table; + m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; error: @@ -1357,7 +1359,7 @@ error: bool Sql_cmd_optimize_table::execute(THD *thd) { LEX *m_lex= thd->lex; - TABLE_LIST *first_table= m_lex->select_lex.table_list.first; + TABLE_LIST *first_table= m_lex->first_select_lex()->table_list.first; bool res= TRUE; DBUG_ENTER("Sql_cmd_optimize_table::execute"); @@ -1379,11 +1381,13 @@ bool Sql_cmd_optimize_table::execute(THD *thd) */ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); } - m_lex->select_lex.table_list.first= first_table; + m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; error: -WSREP_ERROR_LABEL: +#ifdef WITH_WSREP +wsrep_error_label: +#endif DBUG_RETURN(res); } @@ -1391,7 +1395,7 @@ WSREP_ERROR_LABEL: bool Sql_cmd_repair_table::execute(THD *thd) { LEX *m_lex= thd->lex; - TABLE_LIST *first_table= m_lex->select_lex.table_list.first; + TABLE_LIST *first_table= m_lex->first_select_lex()->table_list.first; bool res= TRUE; DBUG_ENTER("Sql_cmd_repair_table::execute"); @@ -1415,10 +1419,12 @@ bool Sql_cmd_repair_table::execute(THD *thd) */ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); } - m_lex->select_lex.table_list.first= first_table; + m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; error: -WSREP_ERROR_LABEL: +#ifdef WITH_WSREP +wsrep_error_label: +#endif DBUG_RETURN(res); } diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 5fc9ff8209c..05a71d7785d 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -356,7 +356,7 @@ bool Sql_cmd_alter_table::execute(THD *thd) { LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); /* first table of first SELECT_LEX */ TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first; /* @@ -497,16 +497,17 @@ bool Sql_cmd_alter_table::execute(THD *thd) lex->ignore); DBUG_RETURN(result); - -WSREP_ERROR_LABEL: +#ifdef WITH_WSREP +wsrep_error_label: WSREP_WARN("ALTER TABLE isolation failure"); DBUG_RETURN(TRUE); +#endif } bool Sql_cmd_discard_import_tablespace::execute(THD *thd) { /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); /* first table of first SELECT_LEX */ TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first; diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 6626a054052..a7cfaca0d0e 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -68,6 +68,25 @@ int compare_decimal2(int* len, const char *s, const char *t) } +static bool +prepare_param(THD *thd, Item **item, const char *proc_name, uint pos) +{ + if ((*item)->fix_fields_if_needed(thd, item)) + { + DBUG_PRINT("info", ("fix_fields() for the parameter %u failed", pos)); + return true; + } + if ((*item)->type_handler()->result_type() != INT_RESULT || + !(*item)->basic_const_item() || + (*item)->val_real() < 0) + { + my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name); + return true; + } + return false; +} + + Procedure * proc_analyse_init(THD *thd, ORDER *param, select_result *result, List<Item> &field_list) @@ -88,17 +107,8 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, else if (param->next) { // first parameter - if ((*param->item)->fix_fields_if_needed(thd, param->item)) - { - DBUG_PRINT("info", ("fix_fields() for the first parameter failed")); - goto err; - } - if ((*param->item)->type() != Item::INT_ITEM || - (*param->item)->val_real() < 0) - { - my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name); + if (prepare_param(thd, param->item, proc_name, 0)) goto err; - } pc->max_tree_elements = (uint) (*param->item)->val_int(); param = param->next; if (param->next) // no third parameter possible @@ -107,25 +117,12 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, goto err; } // second parameter - if ((*param->item)->fix_fields_if_needed(thd, param->item)) - { - DBUG_PRINT("info", ("fix_fields() for the second parameter failed")); - goto err; - } - if ((*param->item)->type() != Item::INT_ITEM || - (*param->item)->val_real() < 0) - { - my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name); + if (prepare_param(thd, param->item, proc_name, 1)) goto err; - } pc->max_treemem = (uint) (*param->item)->val_int(); } - else if ((*param->item)->type() != Item::INT_ITEM || - (*param->item)->val_real() < 0) - { - my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name); + else if (prepare_param(thd, param->item, proc_name, 0)) goto err; - } // if only one parameter was given, it will be the value of max_tree_elements else { @@ -481,30 +478,28 @@ void field_real::add() void field_decimal::add() { /*TODO - remove rounding stuff after decimal_div returns proper frac */ - my_decimal dec_buf, *dec= item->val_decimal(&dec_buf); - my_decimal rounded; + VDec vdec(item); uint length; TREE_ELEMENT *element; - if (item->null_value) + if (vdec.is_null()) { nulls++; return; } - my_decimal_round(E_DEC_FATAL_ERROR, dec, item->decimals, FALSE,&rounded); - dec= &rounded; + my_decimal dec; + vdec.round_to(&dec, item->decimals, HALF_UP); - length= my_decimal_string_length(dec); + length= my_decimal_string_length(&dec); - if (decimal_is_zero(dec)) + if (decimal_is_zero(&dec)) empty++; if (room_in_tree) { uchar buf[DECIMAL_MAX_FIELD_SIZE]; - my_decimal2binary(E_DEC_FATAL_ERROR, dec, buf, - item->max_length, item->decimals); + dec.to_binary(buf, item->max_length, item->decimals); if (!(element = tree_insert(&tree, (void*)buf, 0, tree.custom_arg))) { room_in_tree = 0; // Remove tree, out of RAM ? @@ -524,18 +519,18 @@ void field_decimal::add() if (!found) { found = 1; - min_arg = max_arg = sum[0] = *dec; - my_decimal_mul(E_DEC_FATAL_ERROR, sum_sqr, dec, dec); + min_arg = max_arg = sum[0] = dec; + my_decimal_mul(E_DEC_FATAL_ERROR, sum_sqr, &dec, &dec); cur_sum= 0; min_length = max_length = length; } - else if (!decimal_is_zero(dec)) + else if (!decimal_is_zero(&dec)) { int next_cur_sum= cur_sum ^ 1; my_decimal sqr_buf; - my_decimal_add(E_DEC_FATAL_ERROR, sum+next_cur_sum, sum+cur_sum, dec); - my_decimal_mul(E_DEC_FATAL_ERROR, &sqr_buf, dec, dec); + my_decimal_add(E_DEC_FATAL_ERROR, sum+next_cur_sum, sum+cur_sum, &dec); + my_decimal_mul(E_DEC_FATAL_ERROR, &sqr_buf, &dec, &dec); my_decimal_add(E_DEC_FATAL_ERROR, sum_sqr+next_cur_sum, sum_sqr+cur_sum, &sqr_buf); cur_sum= next_cur_sum; @@ -543,13 +538,13 @@ void field_decimal::add() min_length = length; if (length > max_length) max_length = length; - if (my_decimal_cmp(dec, &min_arg) < 0) + if (dec.cmp(&min_arg) < 0) { - min_arg= *dec; + min_arg= dec; } - if (my_decimal_cmp(dec, &max_arg) > 0) + if (dec.cmp(&max_arg) > 0) { - max_arg= *dec; + max_arg= dec; } } } @@ -1003,7 +998,7 @@ void field_decimal::get_opt_type(String *answer, uint length; my_decimal_set_zero(&zero); - my_bool is_unsigned= (my_decimal_cmp(&zero, &min_arg) >= 0); + my_bool is_unsigned= (zero.cmp(&min_arg) >= 0); length= sprintf(buff, "DECIMAL(%d, %d)", (int) (max_length - (item->decimals ? 1 : 0)), @@ -1016,14 +1011,14 @@ void field_decimal::get_opt_type(String *answer, String *field_decimal::get_min_arg(String *str) { - my_decimal2string(E_DEC_FATAL_ERROR, &min_arg, 0, 0, '0', str); + min_arg.to_string_native(str, 0, 0, '0'); return str; } String *field_decimal::get_max_arg(String *str) { - my_decimal2string(E_DEC_FATAL_ERROR, &max_arg, 0, 0, '0', str); + max_arg.to_string_native(str, 0, 0, '0'); return str; } @@ -1041,10 +1036,10 @@ String *field_decimal::avg(String *s, ha_rows rows) int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num); my_decimal_div(E_DEC_FATAL_ERROR, &avg_val, sum+cur_sum, &num, prec_increment); /* TODO remove this after decimal_div returns proper frac */ - my_decimal_round(E_DEC_FATAL_ERROR, &avg_val, + avg_val.round_to(&rounded_avg, MY_MIN(sum[cur_sum].frac + prec_increment, DECIMAL_MAX_SCALE), - FALSE,&rounded_avg); - my_decimal2string(E_DEC_FATAL_ERROR, &rounded_avg, 0, 0, '0', s); + HALF_UP); + rounded_avg.to_string_native(s, 0, 0, '0'); return s; } @@ -1057,7 +1052,6 @@ String *field_decimal::std(String *s, ha_rows rows) return s; } my_decimal num, tmp, sum2, sum2d; - double std_sqr; int prec_increment= current_thd->variables.div_precincrement; int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num); @@ -1065,7 +1059,7 @@ String *field_decimal::std(String *s, ha_rows rows) my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment); my_decimal_sub(E_DEC_FATAL_ERROR, &sum2, sum_sqr+cur_sum, &tmp); my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment); - my_decimal2double(E_DEC_FATAL_ERROR, &tmp, &std_sqr); + double std_sqr= tmp.to_double(); s->set_real(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)), MY_MIN(item->decimals + prec_increment, NOT_FIXED_DEC), my_thd_charset); @@ -1117,12 +1111,9 @@ int collect_decimal(uchar *element, element_count count, info->str->append(','); else info->found = 1; - my_decimal dec; - binary2my_decimal(E_DEC_FATAL_ERROR, element, &dec, - info->item->max_length, info->item->decimals); - + my_decimal dec(element, info->item->max_length, info->item->decimals); info->str->append('\''); - my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, '0', &s); + dec.to_string_native(&s, 0, 0, '0'); info->str->append(s); info->str->append('\''); return 0; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 52259e4b9e2..7b466bc9c74 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -598,6 +598,7 @@ bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection) static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) { + DBUG_ENTER("mark_used_tables_as_free_for_reuse"); for (; table ; table= table->next) { DBUG_ASSERT(table->pos_in_locked_tables == NULL || @@ -608,6 +609,7 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) table->file->ha_reset(); } } + DBUG_VOID_RETURN; } @@ -4295,7 +4297,9 @@ restart: } error: -WSREP_ERROR_LABEL: +#ifdef WITH_WSREP +wsrep_error_label: +#endif THD_STAGE_INFO(thd, stage_after_opening_tables); thd_proc_info(thd, 0); @@ -5471,43 +5475,27 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) DBUG_ENTER("update_field_dependencies"); if (should_mark_column(thd->column_usage)) { - MY_BITMAP *bitmap; - /* We always want to register the used keys, as the column bitmap may have been set for all fields (for example for view). */ - table->covering_keys.intersect(field->part_of_key); - if (field->vcol_info) - table->mark_virtual_col(field); - if (thd->column_usage == MARK_COLUMNS_READ) - bitmap= table->read_set; + { + if (table->mark_column_with_deps(field)) + DBUG_VOID_RETURN; // Field was already marked + } else - bitmap= table->write_set; - - /* - The test-and-set mechanism in the bitmap is not reliable during - multi-UPDATE statements under MARK_COLUMNS_READ mode - (thd->column_usage == MARK_COLUMNS_READ), as this bitmap contains - only those columns that are used in the SET clause. I.e they are being - set here. See multi_update::prepare() - */ - if (bitmap_fast_test_and_set(bitmap, field->field_index)) { - if (thd->column_usage == MARK_COLUMNS_WRITE) + if (bitmap_fast_test_and_set(table->write_set, field->field_index)) { DBUG_PRINT("warning", ("Found duplicated field")); thd->dup_field= field; + DBUG_VOID_RETURN; } - else - { - DBUG_PRINT("note", ("Field found before")); - } - DBUG_VOID_RETURN; } + table->used_fields++; } if (table->get_fields_in_item_tree) @@ -7437,7 +7425,7 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, Item_window_func::split_sum_func. */ if (sum_func_list && - ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) || + ((item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM) || item->with_window_func)) { item->split_sum_func(thd, ref_pointer_array, *sum_func_list, @@ -7545,7 +7533,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, TABLE_LIST *first_select_table= (select_insert ? tables->next_local: 0); - SELECT_LEX *select_lex= select_insert ? &thd->lex->select_lex : + SELECT_LEX *select_lex= select_insert ? thd->lex->first_select_lex() : thd->lex->current_select; if (select_lex->first_cond_optimization) { @@ -7573,7 +7561,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, { /* new counting for SELECT of INSERT ... SELECT command */ first_select_table= 0; - thd->lex->select_lex.insert_tables= tablenr; + thd->lex->first_select_lex()->insert_tables= tablenr; tablenr= 0; } if(table_list->jtbm_subselect) @@ -7940,18 +7928,9 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if ((field= field_iterator.field())) { - /* Mark fields as used to allow storage engine to optimze access */ - bitmap_set_bit(field->table->read_set, field->field_index); - /* - Mark virtual fields for write and others that the virtual fields - depend on for read. - */ - if (field->vcol_info) - field->table->mark_virtual_col(field); + field->table->mark_column_with_deps(field); if (table) - { table->covering_keys.intersect(field->part_of_key); - } if (tables->is_natural_join) { TABLE *field_table; @@ -8129,7 +8108,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, from subquery of VIEW, because tables of subquery belongs to VIEW (see condition before prepare_check_option() call) */ - bool it_is_update= (select_lex == &thd->lex->select_lex) && + bool it_is_update= (select_lex == thd->lex->first_select_lex()) && thd->lex->which_check_option_applicable(); bool save_is_item_list_lookup= select_lex->is_item_list_lookup; TABLE_LIST *derived= select_lex->master_unit()->derived; @@ -8145,7 +8124,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, for (table= tables; table; table= table->next_local) { - if (select_lex == &thd->lex->select_lex && + if (select_lex == thd->lex->first_select_lex() && select_lex->first_cond_optimization && table->merged_for_insert && table->prepare_where(thd, conds, FALSE)) @@ -8752,7 +8731,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) Item_func_match *ifm; while ((ifm=li++)) - if (unlikely(!ifm->fixed)) + if (unlikely(!ifm->is_fixed())) /* it mean that clause where was FT function was removed, so we have to remove the function from the list. @@ -8854,6 +8833,13 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, void close_system_tables(THD *thd, Open_tables_backup *backup) { + /* + Inform the transaction handler that we are closing the + system tables and we don't need the read view anymore. + */ + for (TABLE *table= thd->open_tables ; table ; table= table->next) + table->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE); + close_thread_tables(thd); thd->restore_backup_open_tables_state(backup); } @@ -8987,7 +8973,7 @@ void unfix_fields(List<Item> &fields) List_iterator<Item> li(fields); Item *item; while ((item= li++)) - item->fixed= 0; + item->unfix_fields(); } diff --git a/sql/sql_base.h b/sql/sql_base.h index 22247af07a8..0ebed4159ab 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -371,10 +371,12 @@ inline bool setup_fields_with_no_wrap(THD *thd, Ref_ptr_array ref_pointer_array, bool allow_sum_func) { bool res; - thd->lex->select_lex.no_wrap_view_item= TRUE; + SELECT_LEX *first= thd->lex->first_select_lex(); + DBUG_ASSERT(thd->lex->current_select == first); + first->no_wrap_view_item= TRUE; res= setup_fields(thd, ref_pointer_array, item, column_usage, sum_func_list, NULL, allow_sum_func); - thd->lex->select_lex.no_wrap_view_item= FALSE; + first->no_wrap_view_item= FALSE; return res; } diff --git a/sql/sql_basic_types.h b/sql/sql_basic_types.h index 1e97262cdf0..362ab0f1259 100644 --- a/sql/sql_basic_types.h +++ b/sql/sql_basic_types.h @@ -22,4 +22,87 @@ typedef ulonglong sql_mode_t; typedef int64 query_id_t; + + +/* + "fuzzydate" with strict data type control. + Please keep "explicit" in constructors and conversion methods. +*/ +class date_mode_t +{ +public: + enum value_t + { + FUZZY_DATES= 1U, // C_TIME_FUZZY_DATES + TIME_ONLY= 4U, // C_TIME_TIME_ONLY + NO_ZERO_IN_DATE= (1UL << 23), // MODE_NO_ZERO_IN_DATE + NO_ZERO_DATE= (1UL << 24), // MODE_NO_ZERO_DATE + INVALID_DATES= (1UL << 25) // MODE_INVALID_DATES + }; + +private: + value_t m_mode; +public: + + // Constructors + explicit date_mode_t(ulonglong fuzzydate) + :m_mode((value_t) fuzzydate) + { } + + // Conversion operators + explicit operator ulonglong() const + { + return m_mode; + } + explicit operator bool() const + { + return m_mode != 0; + } + + // Unary operators + date_mode_t operator~() const + { + return date_mode_t(~m_mode); + } + + // Dyadic bitwise operators + date_mode_t operator&(const date_mode_t &other) const + { + return date_mode_t(m_mode & other.m_mode); + } + + date_mode_t operator|(const date_mode_t &other) const + { + return date_mode_t(m_mode | other.m_mode); + } + + // Dyadic bitwise assignment operators + date_mode_t &operator&=(const date_mode_t &other) + { + m_mode= value_t(m_mode & other.m_mode); + return *this; + } + + date_mode_t &operator|=(const date_mode_t &other) + { + m_mode= value_t(m_mode | other.m_mode); + return *this; + } +}; + + +const date_mode_t + TIME_FUZZY_DATES (date_mode_t::value_t::FUZZY_DATES), + TIME_TIME_ONLY (date_mode_t::value_t::TIME_ONLY), + TIME_NO_ZERO_IN_DATE (date_mode_t::value_t::NO_ZERO_IN_DATE), + TIME_NO_ZERO_DATE (date_mode_t::value_t::NO_ZERO_DATE), + TIME_INVALID_DATES (date_mode_t::value_t::INVALID_DATES); + +// Flags understood by str_to_datetime, str_to_time, number_to_time, check_date +static const date_mode_t + TIME_MODE_FOR_XXX_TO_DATE (date_mode_t::NO_ZERO_IN_DATE | + date_mode_t::NO_ZERO_DATE | + date_mode_t::INVALID_DATES | + date_mode_t::TIME_ONLY); + #endif diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 00270fb6f32..1fa3cca7c27 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -173,7 +173,7 @@ void mysql_client_binlog_statement(THD* thd) */ if (!(rli && buf)) { - my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1); /* needed 1 bytes */ + my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 1); /* needed 1 bytes */ goto end; } diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index aa4c77d0939..44211fca506 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -480,8 +480,7 @@ static void make_base_query(String *new_query, /* We do not support UCS2, UTF16, UTF32 as a client character set */ DBUG_ASSERT(current_thd->variables.character_set_client->mbminlen == 1); - new_query->length(0); // Don't copy anything from old buffer - if (new_query->realloc(query_length + additional_length)) + if (new_query->alloc(query_length + additional_length)) { /* We could not allocate the query. Use original query for @@ -4147,13 +4146,13 @@ Query_cache::is_cacheable(THD *thd, LEX *lex, if (thd->lex->safe_to_cache_query && (thd->variables.query_cache_type == 1 || - (thd->variables.query_cache_type == 2 && (lex->select_lex.options & - OPTION_TO_QUERY_CACHE))) && + (thd->variables.query_cache_type == 2 && + (lex->first_select_lex()->options & OPTION_TO_QUERY_CACHE))) && qc_is_able_to_intercept_result(thd)) { DBUG_PRINT("qcache", ("options: %lx %lx type: %u", (long) OPTION_TO_QUERY_CACHE, - (long) lex->select_lex.options, + (long) lex->first_select_lex()->options, (int) thd->variables.query_cache_type)); if (!(table_count= process_and_count_tables(thd, tables_used, @@ -4174,7 +4173,7 @@ Query_cache::is_cacheable(THD *thd, LEX *lex, ("not interesting query: %d or not cacheable, options %lx %lx type: %u net->vio present: %u", (int) lex->sql_command, (long) OPTION_TO_QUERY_CACHE, - (long) lex->select_lex.options, + (long) lex->first_select_lex()->options, (int) thd->variables.query_cache_type, (uint) MY_TEST(qc_is_able_to_intercept_result(thd)))); DBUG_RETURN(0); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index e4f3171bd14..52ebc186b1a 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1411,7 +1411,7 @@ bool THD::set_db(const LEX_CSTRING *new_db) const char *tmp= NULL; if (new_db->str) { - if (!(tmp= my_strndup(new_db->str, new_db->length, MYF(MY_WME | ME_FATALERROR)))) + if (!(tmp= my_strndup(new_db->str, new_db->length, MYF(MY_WME | ME_FATAL)))) result= 1; } @@ -2570,7 +2570,7 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, size_t key_length) key_length + 1); if (!new_table) { - my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_FATALERROR), + my_error(EE_OUTOFMEMORY, MYF(ME_FATAL), ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1); set_killed(KILL_CONNECTION); return 0; @@ -3225,9 +3225,9 @@ int select_export::send_data(List<Item> &items) ((uint64) res->length() / res->charset()->mbminlen + 1) * write_cs->mbmaxlen + 1; set_if_smaller(estimated_bytes, UINT_MAX32); - if (cvt_str.realloc((uint32) estimated_bytes)) + if (cvt_str.alloc((uint32) estimated_bytes)) { - my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), (uint32) estimated_bytes); + my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), (uint32) estimated_bytes); goto err; } @@ -3485,7 +3485,7 @@ int select_singlerow_subselect::send_data(List<Item> &items) if (it->assigned()) { my_message(ER_SUBQUERY_NO_1_ROW, ER_THD(thd, ER_SUBQUERY_NO_1_ROW), - MYF(current_thd->lex->ignore ? ME_JUST_WARNING : 0)); + MYF(current_thd->lex->ignore ? ME_WARNING : 0)); DBUG_RETURN(1); } if (unit->offset_limit_cnt) @@ -3592,18 +3592,15 @@ bool select_max_min_finder_subselect::cmp_int() bool select_max_min_finder_subselect::cmp_decimal() { Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); - my_decimal cval, *cvalue= cache->val_decimal(&cval); - my_decimal mval, *mvalue= maxmin->val_decimal(&mval); + VDec cvalue(cache), mvalue(maxmin); /* Ignore NULLs for ANY and keep them for ALL subqueries */ - if (cache->null_value) - return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value); - if (maxmin->null_value) + if (cvalue.is_null()) + return (is_all && !mvalue.is_null()) || (!is_all && mvalue.is_null()); + if (mvalue.is_null()) return !is_all; - if (fmax) - return (my_decimal_cmp(cvalue, mvalue) > 0) ; - return (my_decimal_cmp(cvalue,mvalue) < 0); + return fmax ? cvalue.cmp(mvalue) > 0 : cvalue.cmp(mvalue) < 0; } bool select_max_min_finder_subselect::cmp_str() @@ -7712,7 +7709,7 @@ Query_arena_stmt::~Query_arena_stmt() bool THD::timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts, - ulong sec_part, ulonglong fuzzydate) + ulong sec_part, date_mode_t fuzzydate) { time_zone_used= 1; if (ts == 0 && sec_part == 0) diff --git a/sql/sql_class.h b/sql/sql_class.h index acd48b07900..d8f0a794222 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -718,6 +718,7 @@ typedef struct system_variables ulong session_track_transaction_info; my_bool session_track_schema; my_bool session_track_state_change; + my_bool tcp_nodelay; ulong threadpool_priority; @@ -3123,6 +3124,9 @@ public: it returned an error on master, and this is OK on the slave. */ bool is_slave_error; + /* True if we have printed something to the error log for this statement */ + bool error_printed_to_log; + /* True when a transaction is queued up for binlog group commit. Used so that if another transaction needs to wait for a row lock held by @@ -3406,7 +3410,7 @@ public: } const Type_handler *type_handler_for_date() const; bool timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts, - ulong sec_part, ulonglong fuzzydate); + ulong sec_part, date_mode_t fuzzydate); inline my_time_t query_start() { return start_time; } inline ulong query_start_sec_part() { query_start_sec_part_used=1; return start_time_sec_part; } @@ -4372,6 +4376,69 @@ private: return raised; } +private: + void push_warning_truncated_priv(Sql_condition::enum_warning_level level, + uint sql_errno, + const char *type_str, const char *val) + { + DBUG_ASSERT(sql_errno == ER_TRUNCATED_WRONG_VALUE || + sql_errno == ER_WRONG_VALUE); + char buff[MYSQL_ERRMSG_SIZE]; + CHARSET_INFO *cs= &my_charset_latin1; + cs->cset->snprintf(cs, buff, sizeof(buff), + ER_THD(this, sql_errno), type_str, val); + /* + Note: the format string can vary between ER_TRUNCATED_WRONG_VALUE + and ER_WRONG_VALUE, but the code passed to push_warning() is + always ER_TRUNCATED_WRONG_VALUE. This is intentional. + */ + push_warning(this, level, ER_TRUNCATED_WRONG_VALUE, buff); + } +public: + void push_warning_truncated_wrong_value(Sql_condition::enum_warning_level level, + const char *type_str, const char *val) + { + return push_warning_truncated_priv(level, ER_TRUNCATED_WRONG_VALUE, + type_str, val); + } + void push_warning_wrong_value(Sql_condition::enum_warning_level level, + const char *type_str, const char *val) + { + return push_warning_truncated_priv(level, ER_WRONG_VALUE, type_str, val); + } + void push_warning_truncated_wrong_value(const char *type_str, const char *val) + { + return push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_WARN, + type_str, val); + } + void push_warning_truncated_value_for_field(Sql_condition::enum_warning_level + level, const char *type_str, + const char *val, const char *name) + { + DBUG_ASSERT(name); + char buff[MYSQL_ERRMSG_SIZE]; + CHARSET_INFO *cs= &my_charset_latin1; + cs->cset->snprintf(cs, buff, sizeof(buff), + ER_THD(this, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), + type_str, val, name, + (ulong) get_stmt_da()->current_row_for_warning()); + push_warning(this, level, ER_TRUNCATED_WRONG_VALUE, buff); + + } + void push_warning_wrong_or_truncated_value(Sql_condition::enum_warning_level level, + bool totally_useless_value, + const char *type_str, + const char *val, + const char *field_name) + { + if (field_name) + push_warning_truncated_value_for_field(level, type_str, val, field_name); + else if (totally_useless_value) + push_warning_wrong_value(level, type_str, val); + else + push_warning_truncated_wrong_value(level, type_str, val); + } + public: /** Overloaded to guard query/query_length fields */ virtual void set_statement(Statement *stmt); @@ -4886,10 +4953,17 @@ my_eof(THD *thd) (A)->variables.sql_log_bin_off= 0;} -inline sql_mode_t sql_mode_for_dates(THD *thd) +inline date_mode_t sql_mode_for_dates(THD *thd) { - return thd->variables.sql_mode & - (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES); + static_assert(C_TIME_FUZZY_DATES == date_mode_t::FUZZY_DATES && + C_TIME_TIME_ONLY == date_mode_t::TIME_ONLY, + "sql_mode_t and pure C library date flags must be equal"); + static_assert(MODE_NO_ZERO_DATE == date_mode_t::NO_ZERO_DATE && + MODE_NO_ZERO_IN_DATE == date_mode_t::NO_ZERO_IN_DATE && + MODE_INVALID_DATES == date_mode_t::INVALID_DATES, + "sql_mode_t and date_mode_t values must be equal"); + return date_mode_t(thd->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES)); } /* @@ -6325,7 +6399,8 @@ public: inline bool add_item_to_list(THD *thd, Item *item) { - return thd->lex->current_select->add_item_to_list(thd, item); + bool res= thd->lex->current_select->add_item_to_list(thd, item); + return res; } inline bool add_value_to_list(THD *thd, Item *value) diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index b05a688f40e..a1af2b6ebbb 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -55,6 +55,14 @@ bool With_clause::add_with_element(With_element *elem) } +void st_select_lex_unit::set_with_clause(With_clause *with_cl) +{ + with_clause= with_cl; + if (with_clause) + with_clause->set_owner(this); +} + + /** @brief Check dependencies between tables defined in a list of with clauses @@ -682,7 +690,7 @@ void With_element::move_anchors_ahead() { st_select_lex *next_sl; st_select_lex *new_pos= spec->first_select(); - new_pos->linkage= UNION_TYPE; + new_pos->set_linkage(UNION_TYPE); for (st_select_lex *sl= new_pos; sl; sl= next_sl) { next_sl= sl->next_select(); @@ -691,9 +699,9 @@ void With_element::move_anchors_ahead() sl->move_node(new_pos); if (new_pos == spec->first_select()) { - enum sub_select_type type= new_pos->linkage; - new_pos->linkage= sl->linkage; - sl->linkage= type; + enum sub_select_type type= new_pos->get_linkage(); + new_pos->set_linkage(sl->get_linkage()); + sl->set_linkage(type); new_pos->with_all_modifier= sl->with_all_modifier; sl->with_all_modifier= false; } @@ -772,7 +780,7 @@ bool With_element::set_unparsed_spec(THD *thd, char *spec_start, char *spec_end, if (!unparsed_spec.str) { - my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), + my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), static_cast<int>(unparsed_spec.length)); return true; } @@ -855,10 +863,9 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd, lex->sp_chistics= old_lex->sp_chistics; lex->stmt_lex= old_lex; - with_select= &lex->select_lex; - with_select->select_number= ++thd->lex->stmt_lex->current_select_number; parse_status= parse_sql(thd, &parser_state, 0); ((char*) &unparsed_spec.str[unparsed_spec.length])[0]= save_end; + with_select= lex->first_select_lex(); if (parse_status) goto err; @@ -1011,7 +1018,7 @@ bool With_element::prepare_unreferenced(THD *thd) rename_columns_of_derived_unit(thd, spec) || check_duplicate_names(thd, first_sl->item_list, 1))) rc= true; - + thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; return rc; } @@ -1122,7 +1129,8 @@ bool TABLE_LIST::set_as_with_table(THD *thd, With_element *with_elem) if(!(derived= with_elem->clone_parsed_spec(thd, this))) return true; } - derived->first_select()->linkage= DERIVED_TABLE_TYPE; + derived->first_select()->set_linkage(DERIVED_TABLE_TYPE); + select_lex->add_statistics(derived); with_elem->inc_references(); return false; } diff --git a/sql/sql_cte.h b/sql/sql_cte.h index bda62271649..03c697bf746 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -292,8 +292,7 @@ private: */ With_clause *next_with_clause; /* Set to true if dependencies between with elements have been checked */ - bool dependencies_are_checked; - + bool dependencies_are_checked; /* The bitmap of all recursive with elements whose specifications are not complied with restrictions imposed by the SQL standards @@ -317,9 +316,8 @@ public: bool with_recursive; With_clause(bool recursive_fl, With_clause *emb_with_clause) - : owner(NULL), - embedding_with_clause(emb_with_clause), next_with_clause(NULL), - dependencies_are_checked(false), unrestricted(0), + : owner(NULL), embedding_with_clause(emb_with_clause), + next_with_clause(NULL), dependencies_are_checked(false), unrestricted(0), with_prepared_anchor(0), cleaned(0), stabilized(0), with_recursive(recursive_fl) { } @@ -333,8 +331,12 @@ public: last_next= &this->next_with_clause; } + st_select_lex_unit *get_owner() { return owner; } + void set_owner(st_select_lex_unit *unit) { owner= unit; } + void attach_to(st_select_lex *select_lex); + With_clause *pop() { return embedding_with_clause; } bool check_dependencies(); @@ -367,7 +369,6 @@ bool With_element::is_unrestricted() } inline - bool With_element::is_with_prepared_anchor() { return owner->with_prepared_anchor & get_elem_map(); @@ -449,11 +450,14 @@ void With_element::prepare_for_next_iteration() inline -void st_select_lex_unit::set_with_clause(With_clause *with_cl) -{ - with_clause= with_cl; - if (with_clause) - with_clause->set_owner(this); +void With_clause::attach_to(st_select_lex *select_lex) +{ + for (With_element *with_elem= with_list.first; + with_elem; + with_elem= with_elem->next) + { + select_lex->register_unit(with_elem->spec, NULL); + } } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index fc400252397..3ae113a4595 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -290,7 +290,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool has_triggers; ORDER *order= (ORDER *) ((order_list && order_list->elements) ? order_list->first : NULL); - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; bool binlog_is_row; @@ -352,7 +352,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); } table->map=1; - query_plan.select_lex= &thd->lex->select_lex; + query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; query_plan.updating_a_view= MY_TEST(table_list->view); @@ -384,7 +384,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, setup_order(thd, select_lex->ref_pointer_array, &tables, fields, all_fields, order)) { - free_underlaid_joins(thd, &thd->lex->select_lex); + free_underlaid_joins(thd, thd->lex->first_select_lex()); DBUG_RETURN(TRUE); } } @@ -770,7 +770,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, else { table->file->print_error(error, - MYF(thd->lex->ignore ? ME_JUST_WARNING : 0)); + MYF(thd->lex->ignore ? ME_WARNING : 0)); if (thd->is_error()) { error= 1; @@ -931,14 +931,16 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, bool *delete_while_scanning) { Item *fake_conds= 0; - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); DBUG_ENTER("mysql_prepare_delete"); List<Item> all_fields; *delete_while_scanning= true; thd->lex->allow_sum_func= 0; - if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, + if (setup_tables_and_check_access(thd, + &thd->lex->first_select_lex()->context, + &thd->lex->first_select_lex()-> + top_join_list, table_list, select_lex->leaf_tables, FALSE, DELETE_ACL, SELECT_ACL, TRUE)) @@ -1021,21 +1023,23 @@ int mysql_multi_delete_prepare(THD *thd) lex->query_tables also point on local list of DELETE SELECT_LEX */ - if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, + if (setup_tables_and_check_access(thd, + &thd->lex->first_select_lex()->context, + &thd->lex->first_select_lex()-> + top_join_list, lex->query_tables, - lex->select_lex.leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL, FALSE)) + lex->first_select_lex()->leaf_tables, + FALSE, DELETE_ACL, SELECT_ACL, FALSE)) DBUG_RETURN(TRUE); - if (lex->select_lex.handle_derived(thd->lex, DT_MERGE)) + if (lex->first_select_lex()->handle_derived(thd->lex, DT_MERGE)) DBUG_RETURN(TRUE); /* Multi-delete can't be constructed over-union => we always have single SELECT on top and have to check underlying SELECTs of it */ - lex->select_lex.exclude_from_table_unique_test= TRUE; + lex->first_select_lex()->exclude_from_table_unique_test= TRUE; /* Fix tables-to-be-deleted-from list to point at opened tables */ for (target_tbl= (TABLE_LIST*) aux_tables; target_tbl; @@ -1077,8 +1081,8 @@ int mysql_multi_delete_prepare(THD *thd) Reset the exclude flag to false so it doesn't interfare with further calls to unique_table */ - lex->select_lex.exclude_from_table_unique_test= FALSE; - + lex->first_select_lex()->exclude_from_table_unique_test= FALSE; + if (lex->save_prep_leaf_tables()) DBUG_RETURN(TRUE); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 589d0214292..878aa715b84 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -99,7 +99,8 @@ mysql_handle_derived(LEX *lex, uint phases) processed normally. */ if (phases == DT_MERGE_FOR_INSERT && - cursor && cursor->top_table()->select_lex != &lex->select_lex) + cursor && (cursor->top_table()->select_lex != + lex->first_select_lex())) continue; for (; cursor && !res; @@ -1242,25 +1243,61 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived) /** @brief - Extract the condition depended on derived table/view and pushed it there + Extract condition that can be pushed into a derived table/view - @param thd The thread handle - @param cond The condition from which to extract the pushed condition - @param derived The reference to the derived table/view + @param thd the thread handle + @param cond current condition + @param derived the reference to the derived table/view @details - This functiom builds the most restrictive condition depending only on - the derived table/view that can be extracted from the condition cond. - The built condition is pushed into the having clauses of the - selects contained in the query specifying the derived table/view. - The function also checks for each select whether any condition depending - only on grouping fields can be extracted from the pushed condition. - If so, it pushes the condition over grouping fields into the where - clause of the select. - - @retval - true if an error is reported - false otherwise + This function builds the most restrictive condition depending only on + the derived table/view (directly or indirectly through equality) that + can be extracted from the given condition cond and pushes it into the + derived table/view. + + Example of the transformation: + + SELECT * + FROM t1, + ( + SELECT x,MAX(y) AS max_y + FROM t2 + GROUP BY x + ) AS d_tab + WHERE d_tab.x>1 AND d_tab.max_y<30; + + => + + SELECT * + FROM t1, + ( + SELECT x,z,MAX(y) AS max_y + FROM t2 + WHERE x>1 + HAVING max_y<30 + GROUP BY x + ) AS d_tab + WHERE d_tab.x>1 AND d_tab.max_y<30; + + In details: + 1. Check what pushable formula can be extracted from cond + 2. Build a clone PC of the formula that can be extracted + (the clone is built only if the extracted formula is a AND subformula + of cond or conjunction of such subformulas) + Do for every select specifying derived table/view: + 3. If there is no HAVING clause prepare PC to be conjuncted with + WHERE clause of the select. Otherwise do 4-7. + 4. Check what formula PC_where can be extracted from PC to be pushed + into the WHERE clause of the select + 5. Build PC_where and if PC_where is a conjunct(s) of PC remove it from PC + getting PC_having + 6. Prepare PC_where to be conjuncted with the WHERE clause of the select + 7. Prepare PC_having to be conjuncted with the HAVING clause of the select + @note + This method is similar to pushdown_cond_for_in_subquery() + + @retval TRUE if an error occurs + @retval FALSE otherwise */ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) @@ -1300,63 +1337,25 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) if (!some_select_allows_cond_pushdown) DBUG_RETURN(false); - /* - Build the most restrictive condition extractable from 'cond' - that can be pushed into the derived table 'derived'. - All subexpressions of this condition are cloned from the - subexpressions of 'cond'. - This condition has to be fixed yet. - */ + /* 1. Check what pushable formula can be extracted from cond */ Item *extracted_cond; - derived->check_pushable_cond_for_table(cond); - extracted_cond= derived->build_pushable_cond_for_table(thd, cond); + cond->check_pushable_cond(&Item::pushable_cond_checker_for_derived, + (uchar *)(&derived->table->map)); + /* 2. Build a clone PC of the formula that can be extracted */ + extracted_cond= + cond->build_pushable_cond(thd, + &Item::pushable_equality_checker_for_derived, + ((uchar *)&derived->table->map)); if (!extracted_cond) { /* Nothing can be pushed into the derived table */ DBUG_RETURN(false); } - /* Push extracted_cond into every select of the unit specifying 'derived' */ + st_select_lex *save_curr_select= thd->lex->current_select; for (; sl; sl= sl->next_select()) { Item *extracted_cond_copy; - if (!sl->cond_pushdown_is_allowed()) - continue; - thd->lex->current_select= sl; - if (sl->have_window_funcs()) - { - if (sl->join->group_list || sl->join->implicit_grouping) - continue; - ORDER *common_partition_fields= - sl->find_common_window_func_partition_fields(thd); - if (!common_partition_fields) - continue; - extracted_cond_copy= !sl->next_select() ? - extracted_cond : - extracted_cond->build_clone(thd); - if (!extracted_cond_copy) - continue; - - Item *cond_over_partition_fields;; - sl->collect_grouping_fields(thd, common_partition_fields); - sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy, - derived); - cond_over_partition_fields= - sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true); - if (cond_over_partition_fields) - cond_over_partition_fields= cond_over_partition_fields->transform(thd, - &Item::derived_grouping_field_transformer_for_where, - (uchar*) sl); - if (cond_over_partition_fields) - { - cond_over_partition_fields->walk( - &Item::cleanup_excluding_const_fields_processor, 0, 0); - sl->cond_pushed_into_where= cond_over_partition_fields; - } - - continue; - } - /* For each select of the unit except the last one create a clone of extracted_cond @@ -1367,71 +1366,43 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) if (!extracted_cond_copy) continue; - if (!sl->join->group_list && !sl->with_sum_func) - { - /* extracted_cond_copy is pushed into where of sl */ - extracted_cond_copy= extracted_cond_copy->transform(thd, - &Item::derived_field_transformer_for_where, - (uchar*) sl); - if (extracted_cond_copy) - { - extracted_cond_copy->walk( - &Item::cleanup_excluding_const_fields_processor, 0, 0); - sl->cond_pushed_into_where= extracted_cond_copy; - } - - continue; - } - - /* - Figure out what can be extracted from the pushed condition - that could be pushed into the where clause of sl - */ - Item *cond_over_grouping_fields; - sl->collect_grouping_fields(thd, sl->join->group_list); - sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy, - derived); - cond_over_grouping_fields= - sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true); - - /* - Transform the references to the 'derived' columns from the condition - pushed into the where clause of sl to make them usable in the new context - */ - if (cond_over_grouping_fields) - cond_over_grouping_fields= cond_over_grouping_fields->transform(thd, - &Item::derived_grouping_field_transformer_for_where, - (uchar*) sl); - - if (cond_over_grouping_fields) + /* Collect fields that are used in the GROUP BY of sl */ + if (sl->have_window_funcs()) { - /* - In extracted_cond_copy remove top conjuncts that - has been pushed into the where clause of sl - */ - extracted_cond_copy= remove_pushed_top_conjuncts(thd, extracted_cond_copy); - - cond_over_grouping_fields->walk( - &Item::cleanup_excluding_const_fields_processor, 0, 0); - sl->cond_pushed_into_where= cond_over_grouping_fields; - - if (!extracted_cond_copy) + if (sl->group_list.first || sl->join->implicit_grouping) continue; + ORDER *common_partition_fields= + sl->find_common_window_func_partition_fields(thd); + if (!common_partition_fields) + continue; + sl->collect_grouping_fields(thd, common_partition_fields); } + else + sl->collect_grouping_fields(thd, sl->group_list.first); + + Item *remaining_cond= NULL; + /* Do 4-6 */ + sl->pushdown_cond_into_where_clause(thd, extracted_cond_copy, + &remaining_cond, + &Item::derived_field_transformer_for_where, + (uchar *) sl); + if (!remaining_cond) + continue; /* - Transform the references to the 'derived' columns from the condition - pushed into the having clause of sl to make them usable in the new context + 7. Prepare PC_having to be conjuncted with the HAVING clause of + the select */ - extracted_cond_copy= extracted_cond_copy->transform(thd, - &Item::derived_field_transformer_for_having, - (uchar*) sl); - if (!extracted_cond_copy) + remaining_cond= + remaining_cond->transform(thd, + &Item::derived_field_transformer_for_having, + (uchar *) sl); + if (!remaining_cond) continue; - extracted_cond_copy->walk(&Item::cleanup_excluding_const_fields_processor, - 0, 0); - sl->cond_pushed_into_having= extracted_cond_copy; + remaining_cond->walk(&Item::cleanup_excluding_const_fields_processor, + 0, 0); + sl->cond_pushed_into_having= remaining_cond; } thd->lex->current_select= save_curr_select; DBUG_RETURN(false); diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 2a4e43ab78a..1652b313909 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -33,7 +33,7 @@ bool mysql_do(THD *thd, List<Item> &values) DBUG_RETURN(TRUE); while ((value = li++)) (void) value->is_null(); - free_underlaid_joins(thd, &thd->lex->select_lex); + free_underlaid_joins(thd, thd->lex->first_select_lex()); if (unlikely(thd->is_error())) { diff --git a/sql/sql_error.cc b/sql/sql_error.cc index d6f5b99eef6..8d639f9271d 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -781,7 +781,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show) List<Item> field_list; MEM_ROOT *mem_root= thd->mem_root; const Sql_condition *err; - SELECT_LEX *sel= &thd->lex->select_lex; + SELECT_LEX *sel= thd->lex->first_select_lex(); SELECT_LEX_UNIT *unit= &thd->lex->unit; ulonglong idx= 0; Protocol *protocol=thd->protocol; diff --git a/sql/sql_error.h b/sql/sql_error.h index 822503f89d3..b783d527cb3 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -846,8 +846,8 @@ public: class ErrConvInteger : public ErrConv, public Longlong_hybrid { public: - ErrConvInteger(longlong num_arg, bool unsigned_flag= false) : - ErrConv(), Longlong_hybrid(num_arg, unsigned_flag) {} + ErrConvInteger(const Longlong_hybrid &nr) + : ErrConv(), Longlong_hybrid(nr) { } const char *ptr() const { return m_unsigned ? ullstr(m_value, err_buffer) : diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 69ea04a170c..72df8367dc7 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -433,8 +433,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) /* Always read all columns */ table->read_set= &table->s->all_set; - if (table->vcol_set) - table->vcol_set= &table->s->all_set; /* Restore the state. */ thd->set_open_tables(backup_open_tables); diff --git a/sql/sql_help.cc b/sql/sql_help.cc index aa9f3fedd6d..95bc6ade366 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -87,7 +87,7 @@ enum enum_used_fields static bool init_fields(THD *thd, TABLE_LIST *tables, struct st_find_field *find_fields, uint count) { - Name_resolution_context *context= &thd->lex->select_lex.context; + Name_resolution_context *context= &thd->lex->first_select_lex()->context; DBUG_ENTER("init_fields"); context->resolve_in_table_list_only(tables); for (; count-- ; find_fields++) @@ -719,10 +719,11 @@ static bool mysqld_help_internal(THD *thd, const char *mask) Init tables and fields to be usable from items tables do not contain VIEWs => we can pass 0 as conds */ - thd->lex->select_lex.context.table_list= - thd->lex->select_lex.context.first_name_resolution_table= &tables[0]; - if (setup_tables(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, + thd->lex->first_select_lex()->context.table_list= + thd->lex->first_select_lex()->context.first_name_resolution_table= + &tables[0]; + if (setup_tables(thd, &thd->lex->first_select_lex()->context, + &thd->lex->first_select_lex()->top_join_list, tables, leaves, FALSE, FALSE)) goto error; memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields)); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 6e2fa5767f5..127b4b10eb4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -241,7 +241,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, } else { // Part field list - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); Name_resolution_context *context= &select_lex->context; Name_resolution_context_state ctx_state; int res; @@ -273,7 +273,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, /* Restore the current context. */ ctx_state.restore_state(context, table_list); - thd->lex->select_lex.no_wrap_view_item= FALSE; + thd->lex->first_select_lex()->no_wrap_view_item= FALSE; if (res) DBUG_RETURN(-1); @@ -657,7 +657,7 @@ static void save_insert_query_plan(THD* thd, TABLE_LIST *table_list) bool skip= MY_TEST(table_list->view); /* Save subquery children */ - for (SELECT_LEX_UNIT *unit= thd->lex->select_lex.first_inner_unit(); + for (SELECT_LEX_UNIT *unit= thd->lex->first_select_lex()->first_inner_unit(); unit; unit= unit->next_unit()) { @@ -777,7 +777,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, /* mysql_prepare_insert sets table_list->table if it was not set */ table= table_list->table; - context= &thd->lex->select_lex.context; + context= &thd->lex->first_select_lex()->context; /* These three asserts test the hypothesis that the resetting of the name resolution context below is not necessary at all since the list of local @@ -1070,7 +1070,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } while (bulk_parameters_iterations(thd)); values_loop_end: - free_underlaid_joins(thd, &thd->lex->select_lex); + free_underlaid_joins(thd, thd->lex->first_select_lex()); joins_freed= TRUE; /* @@ -1259,7 +1259,7 @@ abort: table->file->ha_release_auto_increment(); if (!joins_freed) - free_underlaid_joins(thd, &thd->lex->select_lex); + free_underlaid_joins(thd, thd->lex->first_select_lex()); thd->abort_on_warning= 0; DBUG_RETURN(retval); } @@ -1289,7 +1289,7 @@ abort: static bool check_view_insertability(THD * thd, TABLE_LIST *view) { - uint num= view->view->select_lex.item_list.elements; + uint num= view->view->first_select_lex()->item_list.elements; TABLE *table= view->table; Field_translator *trans_start= view->field_translation, *trans_end= trans_start + num; @@ -1389,10 +1389,12 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, than INSERT. */ - if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, + if (setup_tables_and_check_access(thd, + &thd->lex->first_select_lex()->context, + &thd->lex->first_select_lex()-> + top_join_list, table_list, - thd->lex->select_lex.leaf_tables, + thd->lex->first_select_lex()->leaf_tables, select_insert, INSERT_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(TRUE); @@ -1400,7 +1402,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, if (insert_into_view && !fields.elements) { thd->lex->empty_field_list_on_rset= 1; - if (!thd->lex->select_lex.leaf_tables.head()->table || + if (!thd->lex->first_select_lex()->leaf_tables.head()->table || table_list->is_multitable()) { my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0), @@ -1474,7 +1476,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, enum_duplicates duplic, COND **where, bool select_insert) { - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); Name_resolution_context *context= &select_lex->context; Name_resolution_context_state ctx_state; bool insert_into_view= (table_list->view != 0); @@ -1719,7 +1721,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) */ if (info->ignore) { - table->file->print_error(error, MYF(ME_JUST_WARNING)); + table->file->print_error(error, MYF(ME_WARNING)); goto ok_or_after_trg_err; /* Ignoring a not fatal error, return 0 */ } goto err; @@ -1844,7 +1846,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) { if (!(thd->variables.old_behavior & OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE)) - table->file->print_error(error, MYF(ME_JUST_WARNING)); + table->file->print_error(error, MYF(ME_WARNING)); goto ok_or_after_trg_err; } goto err; @@ -2025,7 +2027,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) goto err; if (!(thd->variables.old_behavior & OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE)) - table->file->print_error(error, MYF(ME_JUST_WARNING)); + table->file->print_error(error, MYF(ME_WARNING)); table->file->restore_auto_increment(prev_insert_id); goto ok_or_after_trg_err; } @@ -2359,7 +2361,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request, di->thd.set_db(&table_list->db); di->thd.set_query(my_strndup(table_list->table_name.str, table_list->table_name.length, - MYF(MY_WME | ME_FATALERROR)), + MYF(MY_WME | ME_FATAL)), table_list->table_name.length, system_charset_info); if (di->thd.db.str == NULL || di->thd.query() == NULL) { @@ -2391,7 +2393,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request, mysql_mutex_unlock(&di->mutex); di->unlock(); delete di; - my_error(ER_CANT_CREATE_THREAD, MYF(ME_FATALERROR), error); + my_error(ER_CANT_CREATE_THREAD, MYF(ME_FATAL), error); goto end_create; } @@ -2605,10 +2607,6 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) share->default_fields) { bool error_reported= FALSE; - if (unlikely(!(copy->def_vcol_set= - (MY_BITMAP*) alloc_root(client_thd->mem_root, - sizeof(MY_BITMAP))))) - goto error; if (unlikely(parse_vcol_defs(client_thd, client_thd->mem_root, copy, &error_reported))) goto error; @@ -2627,15 +2625,6 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) copy->def_write_set.bitmap= ((my_bitmap_map*) (bitmap + share->column_bitmap_size)); bitmaps_used= 2; - if (share->virtual_fields) - { - my_bitmap_init(copy->def_vcol_set, - (my_bitmap_map*) (bitmap + - bitmaps_used*share->column_bitmap_size), - share->fields, FALSE); - bitmaps_used++; - copy->vcol_set= copy->def_vcol_set; - } if (share->default_fields || share->default_expressions) { my_bitmap_init(©->has_value_set, @@ -3261,7 +3250,7 @@ bool Delayed_insert::handle_inserts(void) or if another thread is removing the current table definition from the table cache. */ - my_error(ER_DELAYED_CANT_CHANGE_LOCK, MYF(ME_FATALERROR | ME_NOREFRESH), + my_error(ER_DELAYED_CANT_CHANGE_LOCK, MYF(ME_FATAL | ME_ERROR_LOG), table->s->table_name.str); goto err; } @@ -3434,7 +3423,7 @@ bool Delayed_insert::handle_inserts(void) { /* This is not known to happen. */ my_error(ER_DELAYED_CANT_CHANGE_LOCK, - MYF(ME_FATALERROR | ME_NOREFRESH), + MYF(ME_FATAL | ME_ERROR_LOG), table->s->table_name.str); goto err; } @@ -3530,7 +3519,7 @@ bool Delayed_insert::handle_inserts(void) bool mysql_insert_select_prepare(THD *thd) { LEX *lex= thd->lex; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); DBUG_ENTER("mysql_insert_select_prepare"); @@ -3619,7 +3608,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) select, LEX::current_select should point to the first select while we are fixing fields from insert list. */ - lex->current_select= &lex->select_lex; + lex->current_select= lex->first_select_lex(); res= (setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, NULL, 0) || @@ -3636,7 +3625,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) if (info.handle_duplicates == DUP_UPDATE && !res) { - Name_resolution_context *context= &lex->select_lex.context; + Name_resolution_context *context= &lex->first_select_lex()->context; Name_resolution_context_state ctx_state; /* Save the state of the current name resolution context. */ @@ -3646,7 +3635,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) table_list->next_local= 0; context->resolve_in_table_list_only(table_list); - lex->select_lex.no_wrap_view_item= TRUE; + lex->first_select_lex()->no_wrap_view_item= TRUE; res= res || check_update_fields(thd, context->table_list, *info.update_fields, *info.update_values, @@ -3657,15 +3646,15 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) */ true, &map); - lex->select_lex.no_wrap_view_item= FALSE; + lex->first_select_lex()->no_wrap_view_item= FALSE; /* When we are not using GROUP BY and there are no ungrouped aggregate functions we can refer to other tables in the ON DUPLICATE KEY part. We use next_name_resolution_table descructively, so check it first (views?) */ DBUG_ASSERT (!table_list->next_name_resolution_table); - if (lex->select_lex.group_list.elements == 0 && - !lex->select_lex.with_sum_func) + if (lex->first_select_lex()->group_list.elements == 0 && + !lex->first_select_lex()->with_sum_func) /* We must make a single context out of the two separate name resolution contexts : the INSERT table and the tables in the SELECT part of INSERT ... SELECT. @@ -4081,9 +4070,9 @@ void select_insert::abort_result_set() { Field *Item::create_field_for_create_select(TABLE *table) { - Field *def_field, *tmp_field; - return ::create_tmp_field(table->in_use, table, this, type(), - (Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0); + static Tmp_field_param param(false, false, false, false); + Tmp_field_src src; + return create_tmp_field_ex(table, &src, ¶m); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 11bb50a4231..ba8ed192c97 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -174,7 +174,7 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex) { TABLE_LIST *table_list; Table_ident *table_ident; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); Name_resolution_context *context= &select_lex->context; /* We will call the parser to create a part_info struct based on the @@ -635,6 +635,30 @@ void Lex_input_stream::reduce_digest_token(uint token_left, uint token_right) } } +/** + lex starting operations for builtin select collected together +*/ + +void SELECT_LEX::lex_start(LEX *plex) +{ + SELECT_LEX_UNIT *unit= &plex->unit; + /* 'parent_lex' is used in init_query() so it must be before it. */ + parent_lex= plex; + init_query(); + master= unit; + prev= &unit->slave; + link_next= slave= next= 0; + link_prev= (st_select_lex_node**)&(plex->all_selects_list); + DBUG_ASSERT(!group_list_ptrs); + select_number= 1; + in_sum_expr=0; + ftfunc_list_alloc.empty(); + ftfunc_list= &ftfunc_list_alloc; + group_list.empty(); + order_list.empty(); + gorder_list.empty(); +} + void lex_start(THD *thd) { DBUG_ENTER("lex_start"); @@ -659,18 +683,19 @@ void LEX::start(THD *thd_arg) DBUG_ASSERT(!explain); + builtin_select.lex_start(this); + lex_options= 0; context_stack.empty(); + //empty select_stack + select_stack_top= 0; unit.init_query(); - current_select_number= 1; - select_lex.linkage= UNSPECIFIED_TYPE; - /* 'parent_lex' is used in init_query() so it must be before it. */ - select_lex.parent_lex= this; - select_lex.init_query(); + current_select_number= 0; curr_with_clause= 0; with_clauses_list= 0; with_clauses_list_last_next= &with_clauses_list; clone_spec_offset= 0; create_view= NULL; + field_list.empty(); value_list.empty(); update_list.empty(); set_var_list.empty(); @@ -684,17 +709,8 @@ void LEX::start(THD *thd_arg) auxiliary_table_list.empty(); unit.next= unit.master= unit.link_next= unit.return_to= 0; unit.prev= unit.link_prev= 0; - unit.slave= current_select= all_selects_list= &select_lex; - select_lex.master= &unit; - select_lex.prev= &unit.slave; - select_lex.link_next= select_lex.slave= select_lex.next= 0; - select_lex.link_prev= (st_select_lex_node**)&(all_selects_list); - select_lex.options= 0; - select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; - select_lex.init_order(); - select_lex.group_list.empty(); - if (select_lex.group_list_ptrs) - select_lex.group_list_ptrs->clear(); + unit.slave= current_select= all_selects_list= &builtin_select; + sql_cache= LEX::SQL_CACHE_UNSPECIFIED; describe= 0; analyze_stmt= 0; explain_json= false; @@ -704,14 +720,7 @@ void LEX::start(THD *thd_arg) safe_to_cache_query= 1; parsing_options.reset(); empty_field_list_on_rset= 0; - select_lex.select_number= 1; part_info= 0; - select_lex.in_sum_expr=0; - select_lex.ftfunc_list_alloc.empty(); - select_lex.ftfunc_list= &select_lex.ftfunc_list_alloc; - select_lex.group_list.empty(); - select_lex.order_list.empty(); - select_lex.gorder_list.empty(); m_sql_cmd= NULL; duplicates= DUP_ERROR; ignore= 0; @@ -723,6 +732,8 @@ void LEX::start(THD *thd_arg) query_tables= 0; reset_query_tables_list(FALSE); expr_allows_subselect= TRUE; + selects_allow_into= FALSE; + selects_allow_procedure= FALSE; use_only_table_context= FALSE; parse_vcol_expr= FALSE; check_exists= FALSE; @@ -732,8 +743,8 @@ void LEX::start(THD *thd_arg) name= null_clex_str; event_parse_data= NULL; profile_options= PROFILE_NONE; - nest_level=0 ; - select_lex.nest_level_base= &unit; + nest_level= 0; + builtin_select.nest_level_base= &unit; allow_sum_func= 0; in_sum_func= NULL; @@ -757,6 +768,13 @@ void LEX::start(THD *thd_arg) vers_conditions.empty(); is_lex_started= TRUE; + + next_is_main= FALSE; + next_is_down= FALSE; + + wild= 0; + exchange= 0; + DBUG_VOID_RETURN; } @@ -1273,7 +1291,8 @@ int ORAlex(YYSTYPE *yylval, THD *thd) int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd) { int token; - + const int left_paren= (int) '('; + if (lookahead_token >= 0) { /* @@ -1290,6 +1309,8 @@ int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd) token= lex_one_token(yylval, thd); add_digest_token(token, yylval); + SELECT_LEX *curr_sel= thd->lex->current_select; + switch(token) { case WITH: /* @@ -1338,8 +1359,16 @@ int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd) } break; case VALUES: - if (thd->lex->current_select->parsing_place == IN_UPDATE_ON_DUP_KEY || - thd->lex->current_select->parsing_place == IN_PART_FUNC) + if (curr_sel && + (curr_sel->parsing_place == BEFORE_OPT_LIST || + curr_sel->parsing_place == AFTER_LIST)) + { + curr_sel->parsing_place= NO_MATTER; + break; + } + if (curr_sel && + (curr_sel->parsing_place == IN_UPDATE_ON_DUP_KEY || + curr_sel->parsing_place == IN_PART_FUNC)) return VALUE_SYM; token= lex_one_token(yylval, thd); add_digest_token(token, yylval); @@ -1353,6 +1382,43 @@ int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd) lookahead_token= token; return VALUES; } + case VALUE_SYM: + if (curr_sel && + (curr_sel->parsing_place == BEFORE_OPT_LIST || + curr_sel->parsing_place == AFTER_LIST)) + { + curr_sel->parsing_place= NO_MATTER; + return VALUES; + } + break; + case PARTITION_SYM: + case SELECT_SYM: + case UNION_SYM: + if (curr_sel && + (curr_sel->parsing_place == BEFORE_OPT_LIST || + curr_sel->parsing_place == AFTER_LIST)) + { + curr_sel->parsing_place= NO_MATTER; + } + break; + case left_paren: + if (!curr_sel || + curr_sel->parsing_place != BEFORE_OPT_LIST) + return token; + token= lex_one_token(yylval, thd); + add_digest_token(token, yylval); + lookahead_yylval= yylval; + yylval= NULL; + lookahead_token= token; + curr_sel->parsing_place= NO_MATTER; + if (token == LIKE) + return LEFT_PAREN_LIKE; + if (token == WITH) + return LEFT_PAREN_WITH; + if (token != left_paren && token != SELECT_SYM) + return LEFT_PAREN_ALT; + else + return left_paren; break; default: break; @@ -1689,7 +1755,7 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) return(TEXT_STRING); } case MY_LEX_COMMENT: // Comment - lex->select_lex.options|= OPTION_FOUND_COMMENT; + lex->lex_options|= OPTION_LEX_FOUND_COMMENT; while ((c= yyGet()) != '\n' && c) ; yyUnget(); // Safety against eof state= MY_LEX_START; // Try again @@ -1700,7 +1766,7 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) state= MY_LEX_CHAR; // Probable division break; } - lex->select_lex.options|= OPTION_FOUND_COMMENT; + lex->lex_options|= OPTION_LEX_FOUND_COMMENT; /* Reject '/' '*', since we might need to turn off the echo */ yyUnget(); @@ -2208,8 +2274,8 @@ void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, size_t * prefix_length) void st_select_lex_node::init_query_common() { options= 0; - sql_cache= SQL_CACHE_UNSPECIFIED; - linkage= UNSPECIFIED_TYPE; + set_linkage(UNSPECIFIED_TYPE); + distinct= TRUE; no_table_names_allowed= 0; uncacheable= 0; } @@ -2217,7 +2283,7 @@ void st_select_lex_node::init_query_common() void st_select_lex_unit::init_query() { init_query_common(); - linkage= GLOBAL_OPTIONS_TYPE; + set_linkage(GLOBAL_OPTIONS_TYPE); select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; union_distinct= 0; @@ -2259,16 +2325,6 @@ void st_select_lex::init_query() having_fix_field_for_pushed_cond= 0; context.select_lex= this; context.init(); - /* - Add the name resolution context of the current (sub)query to the - stack of contexts for the whole query. - TODO: - push_context may return an error if there is no memory for a new - element in the stack, however this method has no return value, - thus push_context should be moved to a place where query - initialization is checked for failure. - */ - parent_lex->push_context(&context, parent_lex->thd->mem_root); cond_count= between_count= with_wild= 0; max_equal_elems= 0; ref_pointer_array.reset(); @@ -2314,16 +2370,14 @@ void st_select_lex::init_select() table_join_options= 0; in_sum_expr= with_wild= 0; options= 0; - sql_cache= SQL_CACHE_UNSPECIFIED; ftfunc_list_alloc.empty(); inner_sum_func_list= 0; ftfunc_list= &ftfunc_list_alloc; - order_list.elements= 0; - order_list.first= 0; - order_list.next= &order_list.first; + order_list.empty(); /* Set limit and offset to default values */ select_limit= 0; /* denotes the default limit = HA_POS_ERROR */ offset_limit= 0; /* denotes the default offset = 0 */ + is_set_query_expr_tail= false; with_sum_func= 0; with_all_modifier= 0; is_correlated= 0; @@ -2385,6 +2439,23 @@ void st_select_lex_node::add_slave(st_select_lex_node *slave_arg) } } +void st_select_lex_node::link_chain_down(st_select_lex_node *first) +{ + st_select_lex_node *last_node; + st_select_lex_node *node= first; + do + { + last_node= node; + node->master= this; + node= node->next; + } while (node); + if ((last_node->next= slave)) + { + slave->prev= &last_node->next; + } + first->prev= &slave; + slave= first; +} /* include on level down (but do not link) @@ -2434,7 +2505,7 @@ void st_select_lex_node::fast_exclude() // Remove slave structure for (; slave; slave= slave->next) slave->fast_exclude(); - + } @@ -2908,8 +2979,7 @@ void st_select_lex::print_order(String *str, else { /* replace numeric reference with equivalent for ORDER constant */ - if (order->item[0]->type() == Item::INT_ITEM && - order->item[0]->basic_const_item()) + if (order->item[0]->is_order_clause_position()) { /* make it expression instead of integer constant */ str->append(STRING_WITH_LEN("''")); @@ -3104,6 +3174,7 @@ LEX::LEX() gtid_domain_static_buffer, initial_gtid_domain_buffer_size, initial_gtid_domain_buffer_size, 0); + unit.slave= &builtin_select; } @@ -3130,12 +3201,12 @@ bool LEX::can_be_merged() // TODO: do not forget implement case when select_lex.table_list.elements==0 /* find non VIEW subqueries/unions */ - bool selects_allow_merge= (select_lex.next_select() == 0 && - !(select_lex.uncacheable & + bool selects_allow_merge= (first_select_lex()->next_select() == 0 && + !(first_select_lex()->uncacheable & UNCACHEABLE_RAND)); if (selects_allow_merge) { - for (SELECT_LEX_UNIT *tmp_unit= select_lex.first_inner_unit(); + for (SELECT_LEX_UNIT *tmp_unit= first_select_lex()->first_inner_unit(); tmp_unit; tmp_unit= tmp_unit->next_unit()) { @@ -3152,12 +3223,12 @@ bool LEX::can_be_merged() } return (selects_allow_merge && - select_lex.group_list.elements == 0 && - select_lex.having == 0 && - select_lex.with_sum_func == 0 && - select_lex.table_list.elements >= 1 && - !(select_lex.options & SELECT_DISTINCT) && - select_lex.select_limit == 0); + first_select_lex()->group_list.elements == 0 && + first_select_lex()->having == 0 && + first_select_lex()->with_sum_func == 0 && + first_select_lex()->table_list.elements >= 1 && + !(first_select_lex()->options & SELECT_DISTINCT) && + first_select_lex()->select_limit == 0); } @@ -3510,7 +3581,7 @@ void LEX::set_trg_event_type_for_tables() Do not iterate over sub-selects, only the tables in the outermost SELECT_LEX can be modified, if any. */ - TABLE_LIST *tables= select_lex.get_table_list(); + TABLE_LIST *tables= first_select_lex()->get_table_list(); while (tables) { @@ -3566,12 +3637,13 @@ TABLE_LIST *LEX::unlink_first_table(bool *link_to_local) /* and from local list if it is not empty */ - if ((*link_to_local= MY_TEST(select_lex.table_list.first))) + if ((*link_to_local= MY_TEST(first_select_lex()->table_list.first))) { - select_lex.context.table_list= - select_lex.context.first_name_resolution_table= first->next_local; - select_lex.table_list.first= first->next_local; - select_lex.table_list.elements--; //safety + first_select_lex()->context.table_list= + first_select_lex()->context.first_name_resolution_table= + first->next_local; + first_select_lex()->table_list.first= first->next_local; + first_select_lex()->table_list.elements--; //safety first->next_local= 0; /* Ensure that the global list has the same first table as the local @@ -3602,7 +3674,7 @@ TABLE_LIST *LEX::unlink_first_table(bool *link_to_local) void LEX::first_lists_tables_same() { - TABLE_LIST *first_table= select_lex.table_list.first; + TABLE_LIST *first_table= first_select_lex()->table_list.first; if (query_tables != first_table && first_table != 0) { TABLE_LIST *next; @@ -3627,6 +3699,23 @@ void LEX::first_lists_tables_same() } } +void LEX::fix_first_select_number() +{ + SELECT_LEX *first= first_select_lex(); + if (first && first->select_number != 1) + { + uint num= first->select_number; + for (SELECT_LEX *sel= all_selects_list; + sel; + sel= sel->next_select_in_list()) + { + if (sel->select_number < num) + sel->select_number++; + } + first->select_number= 1; + } +} + /* Link table back that was unlinked with unlink_first_table() @@ -3652,10 +3741,10 @@ void LEX::link_first_table_back(TABLE_LIST *first, if (link_to_local) { - first->next_local= select_lex.table_list.first; - select_lex.context.table_list= first; - select_lex.table_list.first= first; - select_lex.table_list.elements++; //safety + first->next_local= first_select_lex()->table_list.first; + first_select_lex()->context.table_list= first; + first_select_lex()->table_list.first= first; + first_select_lex()->table_list.elements++; //safety } } } @@ -3684,19 +3773,19 @@ void LEX::cleanup_after_one_table_open() NOTE: all units will be connected to thd->lex->select_lex, because we have not UNION on most upper level. */ - if (all_selects_list != &select_lex) + if (all_selects_list != first_select_lex()) { derived_tables= 0; - select_lex.exclude_from_table_unique_test= false; + first_select_lex()->exclude_from_table_unique_test= false; /* cleunup underlying units (units of VIEW) */ - for (SELECT_LEX_UNIT *un= select_lex.first_inner_unit(); + for (SELECT_LEX_UNIT *un= first_select_lex()->first_inner_unit(); un; un= un->next_unit()) un->cleanup(); /* reduce all selects list to default state */ - all_selects_list= &select_lex; + all_selects_list= first_select_lex(); /* remove underlying units (units of VIEW) subtree */ - select_lex.cut_subtree(); + first_select_lex()->cut_subtree(); } } @@ -4351,7 +4440,7 @@ void SELECT_LEX::update_used_tables() tab->covering_keys= tab->s->keys_for_keyread; tab->covering_keys.intersect(tab->keys_in_use_for_query); /* - View/derived was merged. Need to recalculate read_set/vcol_set + View/derived was merged. Need to recalculate read_set bitmaps here. For example: CREATE VIEW v1 AS SELECT f1,f2,f3 FROM t1; SELECT f1 FROM v1; @@ -4360,8 +4449,6 @@ void SELECT_LEX::update_used_tables() be in the read_set. */ bitmap_clear_all(tab->read_set); - if (tab->vcol_set) - bitmap_clear_all(tab->vcol_set); break; } } @@ -4562,7 +4649,7 @@ void st_select_lex::set_explain_type(bool on_the_fly) using_materialization= TRUE; } - if (&master_unit()->thd->lex->select_lex == this) + if (master_unit()->thd->lex->first_select_lex() == this) { type= is_primary ? "PRIMARY" : "SIMPLE"; } @@ -4757,8 +4844,8 @@ bool LEX::save_prep_leaf_tables() Query_arena *arena= thd->stmt_arena, backup; arena= thd->activate_stmt_arena_if_needed(&backup); //It is used for DETETE/UPDATE so top level has only one SELECT - DBUG_ASSERT(select_lex.next_select() == NULL); - bool res= select_lex.save_prep_leaf_tables(thd); + DBUG_ASSERT(first_select_lex()->next_select() == NULL); + bool res= first_select_lex()->save_prep_leaf_tables(thd); if (arena) thd->restore_active_arena(arena, &backup); @@ -5089,8 +5176,13 @@ bool LEX::is_partition_management() const SELECT_LEX *LEX::exclude_last_select() { - DBUG_ENTER("SELECT_LEX::exclude_last_select"); - SELECT_LEX *exclude= current_select; + return exclude_not_first_select(current_select); +} + +SELECT_LEX *LEX::exclude_not_first_select(SELECT_LEX *exclude) +{ + DBUG_ENTER("LEX::exclude_not_first_select"); + DBUG_PRINT("enter", ("exclude %p #%u", exclude, exclude->select_number)); SELECT_LEX_UNIT *unit= exclude->master_unit(); SELECT_LEX *sl; DBUG_ASSERT(unit->first_select() != exclude); @@ -5101,89 +5193,255 @@ SELECT_LEX *LEX::exclude_last_select() DBUG_PRINT("info", ("excl: %p unit: %p prev: %p", exclude, unit, sl)); if (!sl) DBUG_RETURN(NULL); - DBUG_ASSERT(exclude->next_select() == NULL); - exclude->exclude_from_tree(); + DBUG_ASSERT(&sl->next == exclude->prev); + + exclude->prev= NULL; + current_select= sl; DBUG_RETURN(exclude); } -/** - Put given (new) SELECT_LEX level below after currect (last) SELECT +SELECT_LEX_UNIT *LEX::alloc_unit() +{ + SELECT_LEX_UNIT *unit; + DBUG_ENTER("LEX::alloc_unit"); + if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT())) + DBUG_RETURN(NULL); + + unit->init_query(); + /* TODO: reentrant problem */ + unit->thd= thd; + unit->link_next= 0; + unit->link_prev= 0; + /* TODO: remove return_to */ + unit->return_to= NULL; + DBUG_RETURN(unit); +} - LAST SELECT -> DUMMY SELECT - | - V - NEW UNIT - | - V - NEW SELECT - SELECT (*LAST*) ... FROM (SELECT (*NEW*) ... ) +SELECT_LEX *LEX::alloc_select(bool select) +{ + SELECT_LEX *select_lex; + DBUG_ENTER("LEX::alloc_select"); + if (!(select_lex= new (thd->mem_root) SELECT_LEX())) + DBUG_RETURN(NULL); + DBUG_PRINT("info", ("Allocate select: %p #%u statement lex: %p", + select_lex, thd->lex->stmt_lex->current_select_number, + thd->lex->stmt_lex)); + /* + TODO: move following init to constructor when we get rid of builtin + select + */ + select_lex->select_number= ++thd->lex->stmt_lex->current_select_number; + select_lex->parent_lex= this; /* Used in init_query. */ + select_lex->init_query(); + if (select) + select_lex->init_select(); + select_lex->nest_level_base= &this->unit; + select_lex->include_global((st_select_lex_node**)&all_selects_list); + select_lex->context.resolve_in_select_list= TRUE; + DBUG_RETURN(select_lex); +} - @param nselect Select to put one level below +SELECT_LEX_UNIT * +LEX::create_unit(SELECT_LEX *first_sel) +{ + SELECT_LEX_UNIT *unit; + DBUG_ENTER("LEX::create_unit"); - @retval TRUE Error - @retval FALSE OK -*/ + if (!(unit= alloc_unit())) + DBUG_RETURN(NULL); -bool LEX::add_unit_in_brackets(SELECT_LEX *nselect) + unit->register_select_chain(first_sel); + if (first_sel->next_select()) + { + unit->reset_distinct(); + DBUG_ASSERT(!unit->fake_select_lex); + if (unit->add_fake_select_lex(thd)) + DBUG_RETURN(NULL); + } + DBUG_RETURN(unit); +} + +SELECT_LEX_UNIT * +SELECT_LEX::attach_selects_chain(SELECT_LEX *first_sel, + Name_resolution_context *context) { - DBUG_ENTER("LEX::add_unit_in_brackets"); - bool distinct= nselect->master_unit()->union_distinct == nselect; - bool rc= add_select_to_union_list(distinct, nselect->linkage, 0); - if (rc) - DBUG_RETURN(TRUE); - SELECT_LEX* dummy_select= current_select; - dummy_select->automatic_brackets= TRUE; - dummy_select->linkage= nselect->linkage; + SELECT_LEX_UNIT *unit; + DBUG_ENTER("SELECT_LEX::attach_select_chain"); + + if (!(unit= parent_lex->alloc_unit())) + DBUG_RETURN(NULL); + + unit->register_select_chain(first_sel); + register_unit(unit, context); + if (first_sel->next_select()) + { + unit->reset_distinct(); + DBUG_ASSERT(!unit->fake_select_lex); + if (unit->add_fake_select_lex(parent_lex->thd)) + DBUG_RETURN(NULL); + } + + DBUG_RETURN(unit); +} + +SELECT_LEX * +LEX::wrap_unit_into_derived(SELECT_LEX_UNIT *unit) +{ + SELECT_LEX *wrapping_sel; + Table_ident *ti; + DBUG_ENTER("LEX::wrap_unit_into_derived"); + + if (!(wrapping_sel= alloc_select(TRUE))) + DBUG_RETURN(NULL); + Name_resolution_context *context= &wrapping_sel->context; + context->init(); + wrapping_sel->automatic_brackets= FALSE; + + wrapping_sel->register_unit(unit, context); /* stuff dummy SELECT * FROM (...) */ + + if (push_select(wrapping_sel)) // for Items & TABLE_LIST + DBUG_RETURN(NULL); + + /* add SELECT list*/ + { + Item *item= new (thd->mem_root) + Item_field(thd, context, NULL, NULL, &star_clex_str); + if (item == NULL) + goto err; + if (add_item_to_list(thd, item)) + goto err; + (wrapping_sel->with_wild)++; + } + + unit->first_select()->set_linkage(DERIVED_TABLE_TYPE); + + ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + goto err; + { + TABLE_LIST *table_list; + LEX_CSTRING alias; + if (wrapping_sel->make_unique_derived_name(thd, &alias)) + goto err; + + if (!(table_list= wrapping_sel->add_table_to_list(thd, ti, &alias, + 0, TL_READ, + MDL_SHARED_READ))) + goto err; + + context->resolve_in_table_list_only(table_list); + wrapping_sel->add_joined_table(table_list); + } + + pop_select(); + + derived_tables|= DERIVED_SUBQUERY; + + DBUG_RETURN(wrapping_sel); + +err: + pop_select(); + DBUG_RETURN(NULL); +} + +SELECT_LEX *LEX::wrap_select_chain_into_derived(SELECT_LEX *sel) +{ + SELECT_LEX *dummy_select; + SELECT_LEX_UNIT *unit; + Table_ident *ti; + DBUG_ENTER("LEX::wrap_select_chain_into_derived"); + + if (!(dummy_select= alloc_select(TRUE))) + DBUG_RETURN(NULL); Name_resolution_context *context= &dummy_select->context; - context->init(); + dummy_select->automatic_brackets= FALSE; + + if (!(unit= dummy_select->attach_selects_chain(sel, context))) + DBUG_RETURN(NULL); + + /* stuff dummy SELECT * FROM (...) */ + + if (push_select(dummy_select)) // for Items & TABLE_LIST + DBUG_RETURN(NULL); /* add SELECT list*/ - Item *item= new (thd->mem_root) - Item_field(thd, context, NULL, NULL, &star_clex_str); - if (unlikely(item == NULL)) - DBUG_RETURN(TRUE); - if (unlikely(add_item_to_list(thd, item))) - DBUG_RETURN(TRUE); - (dummy_select->with_wild)++; + { + Item *item= new (thd->mem_root) + Item_field(thd, context, NULL, NULL, &star_clex_str); + if (item == NULL) + goto err; + if (add_item_to_list(thd, item)) + goto err; + (dummy_select->with_wild)++; + } - rc= mysql_new_select(this, 1, nselect); - nselect->linkage= DERIVED_TABLE_TYPE; - DBUG_ASSERT(nselect->outer_select() == dummy_select); + sel->set_linkage(DERIVED_TABLE_TYPE); - current_select= dummy_select; - current_select->nest_level--; + ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + goto err; + { + TABLE_LIST *table_list; + LEX_CSTRING alias; + if (dummy_select->make_unique_derived_name(thd, &alias)) + goto err; - SELECT_LEX_UNIT *unit= nselect->master_unit(); - Table_ident *ti= new (thd->mem_root) Table_ident(unit); - if (unlikely(ti == NULL)) - DBUG_RETURN(TRUE); - char buff[10]; - LEX_CSTRING alias; - alias.length= my_snprintf(buff, sizeof(buff), - "__%u", dummy_select->select_number); - alias.str= thd->strmake(buff, alias.length); - if (unlikely(!alias.str)) - DBUG_RETURN(TRUE); + if (!(table_list= dummy_select->add_table_to_list(thd, ti, &alias, + 0, TL_READ, + MDL_SHARED_READ))) + goto err; - TABLE_LIST *table_list; - if (unlikely(!(table_list= - dummy_select->add_table_to_list(thd, ti, &alias, - 0, TL_READ, - MDL_SHARED_READ)))) - DBUG_RETURN(TRUE); - context->resolve_in_table_list_only(table_list); - dummy_select->add_joined_table(table_list); + context->resolve_in_table_list_only(table_list); + dummy_select->add_joined_table(table_list); + } + + pop_select(); derived_tables|= DERIVED_SUBQUERY; - current_select= nselect; - current_select->nest_level++; - DBUG_RETURN(rc); + DBUG_RETURN(dummy_select); + +err: + pop_select(); + DBUG_RETURN(NULL); +} + +bool LEX::push_context(Name_resolution_context *context) +{ + DBUG_ENTER("LEX::push_context"); + DBUG_PRINT("info", ("Context: %p Select: %p (%d)", + context, context->select_lex, + (context->select_lex ? + context->select_lex->select_number: + 0))); + bool res= context_stack.push_front(context, thd->mem_root); + DBUG_RETURN(res); +} + + +SELECT_LEX *LEX::create_priority_nest(SELECT_LEX *first_in_nest) +{ + DBUG_ENTER("LEX::create_priority_nest"); + DBUG_ASSERT(first_in_nest->first_nested); + enum sub_select_type wr_unit_type= first_in_nest->get_linkage(); + bool wr_distinct= first_in_nest->distinct; + SELECT_LEX *attach_to= first_in_nest->first_nested; + attach_to->cut_next(); + SELECT_LEX *wrapper= wrap_select_chain_into_derived(first_in_nest); + if (wrapper) + { + first_in_nest->first_nested= NULL; + wrapper->set_linkage_and_distinct(wr_unit_type, wr_distinct); + wrapper->first_nested= attach_to->first_nested; + wrapper->set_master_unit(attach_to->master_unit()); + attach_to->link_neighbour(wrapper); + } + DBUG_RETURN(wrapper); } @@ -5198,7 +5456,7 @@ bool LEX::add_unit_in_brackets(SELECT_LEX *nselect) void LEX::check_automatic_up(enum sub_select_type type) { if (type != INTERSECT_TYPE && - current_select->linkage == INTERSECT_TYPE && + current_select->get_linkage() == INTERSECT_TYPE && current_select->outer_select() && current_select->outer_select()->automatic_brackets) { @@ -5648,10 +5906,17 @@ bool LEX::sp_for_loop_implicit_cursor_statement(THD *thd, bounds->m_index->sp_lex_in_use= true; sphead->reset_lex(thd, bounds->m_index); DBUG_ASSERT(thd->lex != this); - if (unlikely(!(item= - new (thd->mem_root) Item_field(thd, - thd->lex->current_context(), - NullS, NullS, &name)))) + /* + We pass NULL as Name_resolution_context here. + It's OK, fix_fields() will not be called for this Item_field created. + Item_field is only needed for LEX::sp_for_loop_cursor_declarations() + and is used to transfer the loop index variable name, "rec" in this example: + FOR rec IN (SELECT * FROM t1) + DO + SELECT rec.a, rec.b; + END FOR; + */ + if (!(item= new (thd->mem_root) Item_field(thd, NULL, NullS, NullS, &name))) return true; bounds->m_index->set_item_and_free_list(item, NULL); if (thd->lex->sphead->restore_lex(thd)) @@ -5758,10 +6023,22 @@ bool LEX::sp_for_loop_intrange_declarations(THD *thd, Lex_for_loop_st *loop, const LEX_CSTRING *index, const Lex_for_loop_bounds_st &bounds) { - if (unlikely(!(loop->m_index= - bounds.m_index-> - sp_add_for_loop_variable(thd, index, - bounds.m_index->get_item())))) + Item *item; + if ((item= bounds.m_index->get_item())->type() == Item::FIELD_ITEM) + { + // We're here is the lower bound is unknown identifier + my_error(ER_SP_UNDECLARED_VAR, MYF(0), item->full_name()); + return true; + } + if ((item= bounds.m_upper_bound->get_item())->type() == Item::FIELD_ITEM) + { + // We're here is the upper bound is unknown identifier + my_error(ER_SP_UNDECLARED_VAR, MYF(0), item->full_name()); + return true; + } + if (!(loop->m_index= + bounds.m_index->sp_add_for_loop_variable(thd, index, + bounds.m_index->get_item()))) return true; if (unlikely(!(loop->m_upper_bound= bounds.m_upper_bound-> @@ -6682,7 +6959,6 @@ Item_param *LEX::add_placeholder(THD *thd, const LEX_CSTRING *name, my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); return NULL; } - Query_fragment pos(thd, sphead, start, end); Item_param *item= new (thd->mem_root) Item_param(thd, name, pos.pos(), pos.length()); @@ -6713,6 +6989,38 @@ bool LEX::add_resignal_statement(THD *thd, const sp_condition_value *v) } +/* + Make an Item when an identifier is found in the FOR loop bounds: + FOR rec IN cursor + FOR var IN var1 .. xxx + FOR var IN row1.field1 .. xxx + When we parse the first expression after the "IN" keyword, + we don't know yet if it's a cursor name, or a scalar SP variable name, + or a field of a ROW SP variable. Here we create Item_field to remember + the fully qualified name. Later sp_for_loop_cursor_declarations() + detects how to treat this name properly. +*/ +Item *LEX::create_item_for_loop_bound(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b, + const LEX_CSTRING *c) +{ + /* + Pass NULL as the name resolution context. + This is OK, fix_fields() won't be called for this Item_field. + */ + return new (thd->mem_root) Item_field(thd, NULL, a->str, b->str, c); +} + + +bool LEX::check_expr_allows_fields_or_error(THD *thd, const char *name) const +{ + if (select_stack_top > 0) + return false; // OK, fields are allowed + my_error(ER_BAD_FIELD_ERROR, MYF(0), name, thd->where); + return true; // Error, fields are not allowed +} + Item *LEX::create_item_ident_nospvar(THD *thd, const Lex_ident_sys_st *a, const Lex_ident_sys_st *b) @@ -6735,12 +7043,11 @@ Item *LEX::create_item_ident_nospvar(THD *thd, my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), a->str, thd->where); return NULL; } - if ((current_select->parsing_place != IN_HAVING) || - (current_select->get_in_sum_expr() > 0)) - return new (thd->mem_root) Item_field(thd, current_context(), - NullS, a->str, b); - return new (thd->mem_root) Item_ref(thd, current_context(), - NullS, a->str, b); + + if (current_select->parsing_place == FOR_LOOP_BOUND) + return create_item_for_loop_bound(thd, &null_clex_str, a, b); + + return create_item_ident_field(thd, NullS, a->str, b); } @@ -6952,12 +7259,11 @@ Item *LEX::create_item_ident(THD *thd, my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), b->str, thd->where); return NULL; } - if (current_select->parsing_place != IN_HAVING || - current_select->get_in_sum_expr() > 0) - return new (thd->mem_root) Item_field(thd, current_context(), - schema, b->str, c); - return new (thd->mem_root) Item_ref(thd, current_context(), - schema, b->str, c); + + if (current_select->parsing_place == FOR_LOOP_BOUND) + return create_item_for_loop_bound(thd, &null_clex_str, b, c); + + return create_item_ident_field(thd, schema, b->str, c); } @@ -6990,11 +7296,9 @@ Item *LEX::create_item_limit(THD *thd, const Lex_ident_cli_st *ca) #endif safe_to_cache_query= 0; - if (unlikely(item->type() != Item::INT_ITEM)) - { - my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0)); + if (!item->is_valid_limit_clause_variable_with_error()) return NULL; - } + item->limit_clause_param= true; return item; } @@ -7024,11 +7328,8 @@ Item *LEX::create_item_limit(THD *thd, if (unlikely(!(item= create_item_spvar_row_field(thd, rh, &sa, &sb, spv, ca->pos(), cb->end())))) return NULL; - if (unlikely(item->type() != Item::INT_ITEM)) - { - my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0)); + if (!item->is_valid_limit_clause_variable_with_error()) return NULL; - } item->limit_clause_param= true; return item; } @@ -7048,15 +7349,20 @@ bool LEX::set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val) } -Item *LEX::create_item_ident_nosp(THD *thd, Lex_ident_sys_st *name) +Item *LEX::create_item_ident_field(THD *thd, const char *db, + const char *table, + const Lex_ident_sys_st *name) { + if (check_expr_allows_fields_or_error(thd, name->str)) + return NULL; + if (current_select->parsing_place != IN_HAVING || current_select->get_in_sum_expr() > 0) return new (thd->mem_root) Item_field(thd, current_context(), - NullS, NullS, name); + db, table, name); return new (thd->mem_root) Item_ref(thd, current_context(), - NullS, NullS, name); + db, table, name); } @@ -7106,6 +7412,11 @@ Item *LEX::create_item_ident_sp(THD *thd, Lex_ident_sys_st *name, if (lex_string_eq(name, STRING_WITH_LEN("SQLERRM"))) return new (thd->mem_root) Item_func_sqlerrm(thd); } + + if (current_select->parsing_place == FOR_LOOP_BOUND) + return create_item_for_loop_bound(thd, &null_clex_str, &null_clex_str, + name); + return create_item_ident_nosp(thd, name); } @@ -7399,8 +7710,8 @@ void st_select_lex::collect_grouping_fields(THD *thd, { if ((*ord->item)->eq((Item*)item, 0)) { - Grouping_tmp_field *grouping_tmp_field= - new Grouping_tmp_field(master_unit()->derived->table->field[i], item); + Field_pair *grouping_tmp_field= + new Field_pair(master_unit()->derived->table->field[i], item); grouping_tmp_fields.push_back(grouping_tmp_field); } } @@ -7431,8 +7742,7 @@ void st_select_lex::collect_grouping_fields(THD *thd, */ void -st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond, - TABLE_LIST *derived) +st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond) { cond->clear_extraction_flag(); if (cond->type() == Item::COND_ITEM) @@ -7445,7 +7755,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond, Item *item; while ((item=li++)) { - check_cond_extraction_for_grouping_fields(item, derived); + check_cond_extraction_for_grouping_fields(item); if (item->get_extraction_flag() != NO_EXTRACTION_FL) { count++; @@ -7494,7 +7804,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond, to figure out whether a subformula depends only on these fields or not. @note The built condition C is always implied by the condition cond - (cond => C). The method tries to build the most restictive such + (cond => C). The method tries to build the least restictive such condition (i.e. for any other condition C' such that cond => C' we have C => C'). @note @@ -7570,6 +7880,140 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond, } +bool st_select_lex::set_nest_level(int new_nest_level) +{ + DBUG_ENTER("st_select_lex::set_nest_level"); + DBUG_PRINT("enter", ("select #%d %p nest level: %d", + select_number, this, new_nest_level)); + if (new_nest_level > (int) MAX_SELECT_NESTING) + { + my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT, MYF(0)); + DBUG_RETURN(TRUE); + } + nest_level= new_nest_level; + new_nest_level++; + for (SELECT_LEX_UNIT *u= first_inner_unit(); u; u= u->next_unit()) + { + if (u->set_nest_level(new_nest_level)) + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + +bool st_select_lex_unit::set_nest_level(int new_nest_level) +{ + DBUG_ENTER("st_select_lex_unit::set_nest_level"); + for(SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) + { + if (sl->set_nest_level(new_nest_level)) + DBUG_RETURN(TRUE); + } + if (fake_select_lex && + fake_select_lex->set_nest_level(new_nest_level)) + DBUG_RETURN(TRUE); + DBUG_RETURN(FALSE); +} + + +bool st_select_lex::check_parameters(SELECT_LEX *main_select) +{ + DBUG_ENTER("st_select_lex::check_parameters"); + DBUG_PRINT("enter", ("select #%d %p nest level: %d", + select_number, this, nest_level)); + + + if ((options & OPTION_PROCEDURE_CLAUSE) && + (!parent_lex->selects_allow_procedure || + next_select() != NULL || + this != master_unit()->first_select() || + nest_level != 0)) + { + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "PROCEDURE"); + DBUG_RETURN(TRUE); + } + + if ((options & SELECT_HIGH_PRIORITY) && this != main_select) + { + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "HIGH_PRIORITY"); + DBUG_RETURN(TRUE); + } + if ((options & OPTION_BUFFER_RESULT) && this != main_select) + { + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_BUFFER_RESULT"); + DBUG_RETURN(TRUE); + } + if ((options & OPTION_FOUND_ROWS) && this != main_select) + { + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CALC_FOUND_ROWS"); + DBUG_RETURN(TRUE); + } + if (options & OPTION_NO_QUERY_CACHE) + { + /* + Allow this flag only on the first top-level SELECT statement, if + SQL_CACHE wasn't specified. + */ + if (this != main_select) + { + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE"); + DBUG_RETURN(TRUE); + } + if (parent_lex->sql_cache == LEX::SQL_CACHE) + { + my_error(ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE"); + DBUG_RETURN(TRUE); + } + parent_lex->safe_to_cache_query=0; + parent_lex->sql_cache= LEX::SQL_NO_CACHE; + } + if (options & OPTION_TO_QUERY_CACHE) + { + /* + Allow this flag only on the first top-level SELECT statement, if + SQL_NO_CACHE wasn't specified. + */ + if (this != main_select) + { + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE"); + DBUG_RETURN(TRUE); + } + if (parent_lex->sql_cache == LEX::SQL_NO_CACHE) + { + my_error(ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE"); + DBUG_RETURN(TRUE); + } + parent_lex->safe_to_cache_query=1; + parent_lex->sql_cache= LEX::SQL_CACHE; + } + + for (SELECT_LEX_UNIT *u= first_inner_unit(); u; u= u->next_unit()) + { + if (u->check_parameters(main_select)) + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + +bool st_select_lex_unit::check_parameters(SELECT_LEX *main_select) +{ + for(SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) + { + if (sl->check_parameters(main_select)) + return TRUE; + } + return fake_select_lex && fake_select_lex->check_parameters(main_select); +} + + +bool LEX::check_main_unit_semantics() +{ + if (unit.set_nest_level(0) || + unit.check_parameters(first_select_lex())) + return TRUE; + return FALSE; +} + int set_statement_var_if_exists(THD *thd, const char *var_name, size_t var_name_length, ulonglong value) { @@ -7624,10 +8068,10 @@ bool LEX::create_or_alter_view_finalize(THD *thd, Table_ident *table_ident) { sql_command= SQLCOM_CREATE_VIEW; /* first table in list is target VIEW name */ - if (unlikely(!select_lex.add_table_to_list(thd, table_ident, NULL, + if (!first_select_lex()->add_table_to_list(thd, table_ident, NULL, TL_OPTION_UPDATING, TL_IGNORE, - MDL_EXCLUSIVE))) + MDL_EXCLUSIVE)) return true; query_tables->open_strategy= TABLE_LIST::OPEN_STUB; return false; @@ -7920,6 +8364,130 @@ Item *Lex_trim_st::make_item_func_trim(THD *thd) const } +/** + @brief + Extract from given item a condition pushable into WHERE clause + + @param thd the thread handle + @param cond the item to extract a condition to be pushed + into WHERE + @param remaining_cond the condition that will remain of cond after + the pushdown of its parts into the WHERE clause + @param transformer the transformer callback function to be + applied to the condition so it can be pushed + down into the WHERE clause of this select + @param arg parameter to be passed to the transformer + + @details + This method checks if cond entirely or its parts can be + pushed into the WHERE clause of this select and prepares it for pushing. + + First it checks wherever this select doesn't have any aggregation function + in its projection and GROUP BY clause. If so cond can be entirely + pushed into the WHERE clause of this select but before its fields should + be transformed with transformer_for_where to make it pushable. + + Otherwise the method checks wherever any condition depending only on + grouping fields can be extracted from cond. If there is any it prepares it + for pushing using grouping_field_transformer_for_where and if it happens to + be a conjunct of cond it removes it from cond. It saves the result of + removal in remaining_cond. + The extracted condition is saved in cond_pushed_into_where of this select. + + @note + When looking for pushable condition the method considers only the grouping + fields from the list grouping_tmp_fields whose elements are of the type + Field_pair. This list must be prepared before the call of the + function. + + @note + This method is called for pushdown conditions into materialized + derived tables/views optimization. + Item::derived_field_transformer_for_where is passed as the actual + callback function. + Also it is called for pushdown conditions into materialized IN subqueries. + Item::in_subq_field_transformer_for_where is passed as the actual + callback function. +*/ + +void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond, + Item **remaining_cond, + Item_transformer transformer, + uchar *arg) +{ + if (!cond_pushdown_is_allowed()) + return; + thd->lex->current_select= this; + if (have_window_funcs()) + { + Item *cond_over_partition_fields; + check_cond_extraction_for_grouping_fields(cond); + cond_over_partition_fields= + build_cond_for_grouping_fields(thd, cond, true); + if (cond_over_partition_fields) + cond_over_partition_fields= cond_over_partition_fields->transform(thd, + &Item::grouping_field_transformer_for_where, + (uchar*) this); + if (cond_over_partition_fields) + { + cond_over_partition_fields->walk( + &Item::cleanup_excluding_const_fields_processor, 0, 0); + cond_pushed_into_where= cond_over_partition_fields; + } + + return; + } + + if (!join->group_list && !with_sum_func) + { + cond= + cond->transform(thd, transformer, arg); + if (cond) + { + cond->walk( + &Item::cleanup_excluding_const_fields_processor, 0, 0); + cond_pushed_into_where= cond; + } + + return; + } + + /* + Figure out what can be extracted from cond + that could be pushed into the WHERE clause of this select + */ + Item *cond_over_grouping_fields; + check_cond_extraction_for_grouping_fields(cond); + cond_over_grouping_fields= + build_cond_for_grouping_fields(thd, cond, true); + + /* + Transform the references to the columns from the cond + pushed into the WHERE clause of this select to make them usable in + the new context + */ + if (cond_over_grouping_fields) + cond_over_grouping_fields= cond_over_grouping_fields->transform(thd, + &Item::grouping_field_transformer_for_where, + (uchar*) this); + + if (cond_over_grouping_fields) + { + + /* + In cond remove top conjuncts that has been pushed into the WHERE + clause of this select + */ + cond= remove_pushed_top_conjuncts(thd, cond); + + cond_over_grouping_fields->walk( + &Item::cleanup_excluding_const_fields_processor, 0, 0); + cond_pushed_into_where= cond_over_grouping_fields; + } + + *remaining_cond= cond; +} + Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb, Lex_ident_cli_st *cname, List<Item> *args) { @@ -8186,9 +8754,716 @@ bool LEX::tvc_finalize_derived() thd->parse_error(); return true; } - if (current_select->linkage == GLOBAL_OPTIONS_TYPE || + if (current_select->get_linkage() == GLOBAL_OPTIONS_TYPE || unlikely(mysql_new_select(this, 1, NULL))) return true; - current_select->linkage= DERIVED_TABLE_TYPE; + current_select->set_linkage(DERIVED_TABLE_TYPE); return tvc_finalize(); } + + +void st_select_lex_unit::reset_distinct() +{ + union_distinct= NULL; + for(SELECT_LEX *sl= first_select()->next_select(); + sl; + sl= sl->next_select()) + { + if (sl->distinct) + { + union_distinct= sl; + } + } +} + + +void st_select_lex_unit::fix_distinct(st_select_lex_unit *new_unit) +{ + if (union_distinct) + { + if (this != union_distinct->master_unit()) + { + DBUG_ASSERT(new_unit == union_distinct->master_unit()); + new_unit->union_distinct= union_distinct; + reset_distinct(); + } + else + new_unit->reset_distinct(); + } +} + + +void st_select_lex_unit::register_select_chain(SELECT_LEX *first_sel) +{ + DBUG_ASSERT(first_sel != 0); + slave= first_sel; + first_sel->prev= &slave; + for(SELECT_LEX *sel=first_sel; sel; sel= sel->next_select()) + { + sel->master= (st_select_lex_node *)this; + uncacheable|= sel->uncacheable; + } +} + + +void st_select_lex::register_unit(SELECT_LEX_UNIT *unit, + Name_resolution_context *outer_context) +{ + if ((unit->next= slave)) + slave->prev= &unit->next; + unit->prev= &slave; + slave= unit; + unit->master= this; + uncacheable|= unit->uncacheable; + + for(SELECT_LEX *sel= unit->first_select();sel; sel= sel->next_select()) + { + sel->context.outer_context= outer_context; + } +} + + +void st_select_lex::add_statistics(SELECT_LEX_UNIT *unit) +{ + for (; + unit; + unit= unit->next_unit()) + for(SELECT_LEX *child= unit->first_select(); + child; + child= child->next_select()) + { + /* + A subselect can add fields to an outer select. + Reserve space for them. + */ + select_n_where_fields+= child->select_n_where_fields; + /* + Aggregate functions in having clause may add fields + to an outer select. Count them also. + */ + select_n_having_items+= child->select_n_having_items; + } +} + + +bool LEX::main_select_push() +{ + DBUG_ENTER("LEX::main_select_push"); + current_select_number= 1; + builtin_select.select_number= 1; + if (push_select(&builtin_select)) + DBUG_RETURN(TRUE); + DBUG_RETURN(FALSE); +} + +void Lex_select_lock::set_to(SELECT_LEX *sel) +{ + if (defined_lock) + { + if (sel->master_unit() && + sel == sel->master_unit()->fake_select_lex) + sel->master_unit()->set_lock_to_the_last_select(*this); + else + { + sel->parent_lex->safe_to_cache_query= 0; + if (update_lock) + { + sel->lock_type= TL_WRITE; + sel->set_lock_for_tables(TL_WRITE); + } + else + { + sel->lock_type= TL_READ_WITH_SHARED_LOCKS; + sel->set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS); + } + } + } +} + +bool Lex_order_limit_lock::set_to(SELECT_LEX *sel) +{ + /*TODO: lock */ + //if (lock.defined_lock && sel == sel->master_unit()->fake_select_lex) + // return TRUE; + if (lock.defined_timeout) + { + THD *thd= sel->parent_lex->thd; + if (set_statement_var_if_exists(thd, + C_STRING_WITH_LEN("lock_wait_timeout"), + lock.timeout) || + set_statement_var_if_exists(thd, + C_STRING_WITH_LEN("innodb_lock_wait_timeout"), + lock.timeout)) + return TRUE; + } + lock.set_to(sel); + sel->explicit_limit= limit.explicit_limit; + sel->select_limit= limit.select_limit; + sel->offset_limit= limit.offset_limit; + if (order_list) + { + if (sel->get_linkage() != GLOBAL_OPTIONS_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE && + (sel->get_linkage() != UNION_TYPE || sel->braces)) + { + my_error(ER_WRONG_USAGE, MYF(0), + "CUBE/ROLLUP", "ORDER BY"); + return TRUE; + } + sel->order_list= *(order_list); + } + sel->is_set_query_expr_tail= true; + return FALSE; +} + + +static void change_item_list_context(List<Item> *list, + Name_resolution_context *context) +{ + List_iterator_fast<Item> it (*list); + Item *item; + while((item= it++)) + { + item->walk(&Item::change_context_processor, FALSE, (void *)context); + } +} + + +bool LEX::insert_select_hack(SELECT_LEX *sel) +{ + DBUG_ENTER("LEX::insert_select_hack"); + + DBUG_ASSERT(first_select_lex() == &builtin_select); + DBUG_ASSERT(sel != NULL); + + DBUG_ASSERT(builtin_select.first_inner_unit() == NULL); + + if (builtin_select.link_prev) + { + if ((*builtin_select.link_prev= builtin_select.link_next)) + ((st_select_lex *)builtin_select.link_next)->link_prev= + builtin_select.link_prev; + builtin_select.link_prev= NULL; // indicator of removal + } + + set_main_unit(sel->master_unit()); + + DBUG_ASSERT(builtin_select.table_list.elements == 1); + TABLE_LIST *insert_table= builtin_select.table_list.first; + + if (!(insert_table->next_local= sel->table_list.first)) + { + sel->table_list.next= &insert_table->next_local; + } + sel->table_list.first= insert_table; + sel->table_list.elements++; + insert_table->select_lex= sel; + + sel->context.first_name_resolution_table= insert_table; + builtin_select.context= sel->context; + change_item_list_context(&field_list, &sel->context); + + if (sel->tvc && !sel->next_select() && + (sql_command == SQLCOM_INSERT_SELECT || + sql_command == SQLCOM_REPLACE_SELECT)) + { + DBUG_PRINT("info", ("'Usual' INSERT detected")); + many_values= sel->tvc->lists_of_values; + sel->options= sel->tvc->select_options; + sel->tvc= NULL; + if (sql_command == SQLCOM_INSERT_SELECT) + sql_command= SQLCOM_INSERT; + else + sql_command= SQLCOM_REPLACE; + } + + + for (SELECT_LEX *sel= all_selects_list; + sel; + sel= sel->next_select_in_list()) + { + if (sel->select_number != 1) + sel->select_number--; + }; + + DBUG_RETURN(FALSE); +} + + +/* + Create an Item_singlerow_subselect for a query expression. +*/ +Item *LEX::create_item_query_expression(THD *thd, + const char *tok_start, + st_select_lex_unit *unit) +{ + if (!expr_allows_subselect || sql_command == SQLCOM_PURGE) + { + thd->parse_error(ER_SYNTAX_ERROR, tok_start); + return NULL; + } + + // Add the subtree of subquery to the current SELECT_LEX + SELECT_LEX *curr_sel= select_stack_head(); + DBUG_ASSERT(current_select == curr_sel); + if (!curr_sel) + curr_sel= &builtin_select; + curr_sel->register_unit(unit, &curr_sel->context); + curr_sel->add_statistics(unit); + + return new (thd->mem_root) + Item_singlerow_subselect(thd, unit->first_select()); +} + + +/** + Process unit parsed in brackets +*/ + +bool LEX::parsed_unit_in_brackets(SELECT_LEX_UNIT *unit) +{ + SELECT_LEX *first_in_nest= unit->pre_last_parse->next_select()->first_nested; + if (first_in_nest->first_nested != first_in_nest) + { + /* There is a priority jump starting from first_in_nest */ + if (create_priority_nest(first_in_nest) == NULL) + return true; + } + push_select(unit->fake_select_lex); + return false; +} + + +/** + Process tail of unit parsed in brackets +*/ +SELECT_LEX *LEX::parsed_unit_in_brackets_tail(SELECT_LEX_UNIT *unit, + Lex_order_limit_lock * l) +{ + pop_select(); + if (l) + { + (l)->set_to(unit->fake_select_lex); + } + return unit->first_select(); +} + + +/** + Process select parsed in brackets +*/ + +SELECT_LEX *LEX::parsed_select(SELECT_LEX *sel, Lex_order_limit_lock * l) +{ + pop_select(); + if (l) + { + if (sel->next_select()) + { + SELECT_LEX_UNIT *unit= sel->master_unit(); + if (!unit) + unit= create_unit(sel); + if (!unit) + return NULL; + if (!unit->fake_select_lex->is_set_query_expr_tail) + l->set_to(unit->fake_select_lex); + else + { + sel= wrap_unit_into_derived(unit); + if (!sel) + return NULL; + l->set_to(sel); + } + } + else if (!sel->is_set_query_expr_tail) + { + l->set_to(sel); + } + else + { + SELECT_LEX_UNIT *unit= create_unit(sel); + if (!unit) + return NULL; + sel= wrap_unit_into_derived(unit); + if (!sel) + return NULL; + l->set_to(sel); + } + } + return sel; +} + + +/** + Process select parsed in brackets +*/ + +SELECT_LEX *LEX::parsed_select_in_brackets(SELECT_LEX *sel, + Lex_order_limit_lock * l) +{ + sel->braces= TRUE; + return parsed_select(sel, l); +} + + +SELECT_LEX_UNIT *LEX::parsed_select_expr_start(SELECT_LEX *s1, SELECT_LEX *s2, + enum sub_select_type unit_type, + bool distinct) +{ + SELECT_LEX_UNIT *res; + SELECT_LEX *sel1; + SELECT_LEX *sel2; + if (!s1->next_select()) + sel1= s1; + else + { + sel1= wrap_unit_into_derived(s1->master_unit()); + if (!sel1) + return NULL; + } + if (!s2->next_select()) + sel2= s2; + else + { + sel2= wrap_unit_into_derived(s2->master_unit()); + if (!sel2) + return NULL; + } + sel1->link_neighbour(sel2); + sel2->set_linkage_and_distinct(unit_type, distinct); + sel2->first_nested= sel1->first_nested= sel1; + res= create_unit(sel1); + if (res == NULL) + return NULL; + res->pre_last_parse= sel1; + return res; +} + + +SELECT_LEX_UNIT *LEX::parsed_select_expr_cont(SELECT_LEX_UNIT *unit, + SELECT_LEX *s2, + enum sub_select_type unit_type, + bool distinct, bool oracle) +{ + SELECT_LEX *sel1; + if (!s2->next_select()) + sel1= s2; + else + { + sel1= wrap_unit_into_derived(s2->master_unit()); + if (!sel1) + return NULL; + } + SELECT_LEX *last= unit->pre_last_parse->next_select(); + + int cmp= oracle? 0 : cmp_unit_op(unit_type, last->get_linkage()); + if (cmp == 0) + { + sel1->first_nested= last->first_nested; + } + else if (cmp > 0) + { + last->first_nested= unit->pre_last_parse; + sel1->first_nested= last; + } + else /* cmp < 0 */ + { + SELECT_LEX *first_in_nest= last->first_nested; + if (first_in_nest->first_nested != first_in_nest) + { + /* There is a priority jump starting from first_in_nest */ + if ((last= create_priority_nest(first_in_nest)) == NULL) + return NULL; + } + sel1->first_nested= last->first_nested; + } + last->link_neighbour(sel1); + sel1->set_master_unit(unit); + sel1->set_linkage_and_distinct(unit_type, distinct); + unit->pre_last_parse= last; + return unit; +} + +/** + Process parsed select in body +*/ + +SELECT_LEX_UNIT *LEX::parsed_body_select(SELECT_LEX *sel, + Lex_order_limit_lock * l) +{ + if (!(sel= parsed_select(sel, l))) + return NULL; + + SELECT_LEX_UNIT *res= create_unit(sel); + return res; +} + +/** + Process parsed unit in body +*/ + +bool LEX::parsed_body_unit(SELECT_LEX_UNIT *unit) +{ + SELECT_LEX *first_in_nest= + unit->pre_last_parse->next_select()->first_nested; + if (first_in_nest->first_nested != first_in_nest) + { + /* There is a priority jump starting from first_in_nest */ + if (create_priority_nest(first_in_nest) == NULL) + return true; + } + push_select(unit->fake_select_lex); + return false; +} + +/** + Process parsed tail of unit in body + + TODO: make processing for double tail case +*/ + +SELECT_LEX_UNIT *LEX::parsed_body_unit_tail(SELECT_LEX_UNIT *unit, + Lex_order_limit_lock * l) +{ + pop_select(); + if (l) + { + (l)->set_to(unit->fake_select_lex); + } + return unit; +} + +/** + Process subselect parsing +*/ + +SELECT_LEX *LEX::parsed_subselect(SELECT_LEX_UNIT *unit, char *place) +{ + if (!expr_allows_subselect || + sql_command == (int)SQLCOM_PURGE) + { + thd->parse_error(ER_SYNTAX_ERROR, place); + return NULL; + } + + // Add the subtree of subquery to the current SELECT_LEX + SELECT_LEX *curr_sel= select_stack_head(); + DBUG_ASSERT(current_select == curr_sel); + if (curr_sel) + { + curr_sel->register_unit(unit, &curr_sel->context); + curr_sel->add_statistics(unit); + } + + return unit->first_select(); +} + + +/** + Process INSERT-like select +*/ + +bool LEX::parsed_insert_select(SELECT_LEX *first_select) +{ + if (sql_command == SQLCOM_INSERT || + sql_command == SQLCOM_REPLACE) + { + if (sql_command == SQLCOM_INSERT) + sql_command= SQLCOM_INSERT_SELECT; + else + sql_command= SQLCOM_REPLACE_SELECT; + } + insert_select_hack(first_select); + if (check_main_unit_semantics()) + return true; + + // fix "main" select + SELECT_LEX *blt= pop_select(); + DBUG_ASSERT(blt == &builtin_select); + push_select(first_select); + return false; +} + + +bool LEX::parsed_TVC_start() +{ + SELECT_LEX *sel; + many_values.empty(); + insert_list= 0; + if (!(sel= alloc_select(TRUE)) || + push_select(sel)) + return true; + sel->init_select(); + sel->braces= FALSE; // just initialisation + return false; +} + + +SELECT_LEX *LEX::parsed_TVC_end() +{ + + SELECT_LEX *res= pop_select(); // above TVC select + if (!(res->tvc= + new (thd->mem_root) table_value_constr(many_values, + res, + res->options))) + return NULL; + many_values.empty(); + return res; +} + + +TABLE_LIST *LEX::parsed_derived_select(SELECT_LEX *sel, int for_system_time, + LEX_CSTRING *alias) +{ + TABLE_LIST *res; + derived_tables|= DERIVED_SUBQUERY; + sel->set_linkage(DERIVED_TABLE_TYPE); + sel->braces= FALSE; + // Add the subtree of subquery to the current SELECT_LEX + SELECT_LEX *curr_sel= select_stack_head(); + DBUG_ASSERT(current_select == curr_sel); + SELECT_LEX_UNIT *unit= sel->master_unit(); + if (!unit) + { + unit= create_unit(sel); + if (!unit) + return NULL; + } + curr_sel->register_unit(unit, &curr_sel->context); + curr_sel->add_statistics(unit); + + Table_ident *ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + return NULL; + if (!(res= curr_sel->add_table_to_list(thd, ti, alias, 0, + TL_READ, MDL_SHARED_READ))) + return NULL; + if (for_system_time) + { + res->vers_conditions= vers_conditions; + } + return res; +} + +TABLE_LIST *LEX::parsed_derived_unit(SELECT_LEX_UNIT *unit, + int for_system_time, + LEX_CSTRING *alias) +{ + TABLE_LIST *res; + derived_tables|= DERIVED_SUBQUERY; + unit->first_select()->set_linkage(DERIVED_TABLE_TYPE); + + // Add the subtree of subquery to the current SELECT_LEX + SELECT_LEX *curr_sel= select_stack_head(); + DBUG_ASSERT(current_select == curr_sel); + curr_sel->register_unit(unit, &curr_sel->context); + curr_sel->add_statistics(unit); + + Table_ident *ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + return NULL; + if (!(res= curr_sel->add_table_to_list(thd, ti, alias, 0, + TL_READ, MDL_SHARED_READ))) + return NULL; + if (for_system_time) + { + res->vers_conditions= vers_conditions; + } + return res; +} + +bool LEX::parsed_create_view(SELECT_LEX_UNIT *unit, int check) +{ + SQL_I_List<TABLE_LIST> *save= &first_select_lex()->table_list; + set_main_unit(unit); + if (check_main_unit_semantics()) + return true; + first_select_lex()->table_list.push_front(save); + current_select= first_select_lex(); + size_t len= thd->m_parser_state->m_lip.get_cpp_ptr() - + create_view->select.str; + void *create_view_select= thd->memdup(create_view->select.str, len); + create_view->select.length= len; + create_view->select.str= (char *) create_view_select; + size_t not_used; + trim_whitespace(thd->charset(), + &create_view->select, ¬_used); + create_view->check= check; + parsing_options.allows_variable= TRUE; + return false; +} + +bool LEX::select_finalize(st_select_lex_unit *expr) +{ + sql_command= SQLCOM_SELECT; + selects_allow_into= TRUE; + selects_allow_procedure= TRUE; + set_main_unit(expr); + return check_main_unit_semantics(); +} + + +/* + "IN" and "EXISTS" subselect can appear in two statement types: + + 1. Statements that can have table columns, such as SELECT, DELETE, UPDATE + 2. Statements that cannot have table columns, e.g: + RETURN ((1) IN (SELECT * FROM t1)) + IF ((1) IN (SELECT * FROM t1)) + + Statements of the first type call master_select_push() in the beginning. + In such case everything is properly linked. + + Statements of the second type do not call mastr_select_push(). + Here we catch the second case and relink thd->lex->builtin_select and + select_lex to properly point to each other. + + QQ: Shouldn't subselects of other type also call relink_hack()? + QQ: Can we do it at constructor time instead? +*/ + +void LEX::relink_hack(st_select_lex *select_lex) +{ + if (!select_stack_top) // Statements of the second type + { + if (!select_lex->get_master()->get_master()) + ((st_select_lex *) select_lex->get_master())-> + set_master(&builtin_select); + if (!builtin_select.get_slave()) + builtin_select.set_slave(select_lex->get_master()); + } +} + + + +bool SELECT_LEX_UNIT::set_lock_to_the_last_select(Lex_select_lock l) +{ + if (l.defined_lock) + { + SELECT_LEX *sel= first_select(); + while (sel->next_select()) + sel= sel->next_select(); + if (sel->braces) + { + my_error(ER_WRONG_USAGE, MYF(0), "lock options", + "End SELECT expression"); + return TRUE; + } + l.set_to(sel); + } + return FALSE; +} + +/** + Generate unique name for generated derived table for this SELECT +*/ + +bool SELECT_LEX::make_unique_derived_name(THD *thd, LEX_CSTRING *alias) +{ + // uint32 digits + two underscores + trailing '\0' + char buff[MAX_INT_WIDTH + 2 + 1]; + alias->length= my_snprintf(buff, sizeof(buff), "__%u", select_number); + alias->str= thd->strmake(buff, alias->length); + return !alias->str; +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 48363fdd670..618214dbb9c 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -162,6 +162,23 @@ public: }; +/** + ORDER BY ... LIMIT parameters; +*/ +class Lex_order_limit_lock: public Sql_alloc +{ +public: + SQL_I_List<st_order> *order_list; /* ORDER clause */ + Lex_select_lock lock; + Lex_select_limit limit; + + Lex_order_limit_lock() :order_list(NULL) + {} + + bool set_to(st_select_lex *sel); +}; + + enum sub_select_type { UNSPECIFIED_TYPE, @@ -169,6 +186,14 @@ enum sub_select_type UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE, GLOBAL_OPTIONS_TYPE, DERIVED_TABLE_TYPE, OLAP_TYPE }; + +inline int cmp_unit_op(enum sub_select_type op1, enum sub_select_type op2) +{ + DBUG_ASSERT(op1 >= UNION_TYPE && op1 <= EXCEPT_TYPE); + DBUG_ASSERT(op2 >= UNION_TYPE && op2 <= EXCEPT_TYPE); + return (op1 == INTERSECT_TYPE ? 1 : 0) - (op2 == INTERSECT_TYPE ? 1 : 0); +} + enum unit_common_op {OP_MIX, OP_UNION, OP_INTERSECT, OP_EXCEPT}; enum enum_view_suid @@ -287,7 +312,8 @@ struct LEX_TYPE This is not within #ifdef because we want "EXPLAIN PARTITIONS ..." to produce additional "partitions" column even if partitioning is not compiled in. */ -#define DESCRIBE_PARTITIONS 4 +#define DESCRIBE_PARTITIONS 4 +#define DESCRIBE_EXTENDED2 8 #ifdef MYSQL_SERVER @@ -526,7 +552,7 @@ public: unit is container of either - One SELECT - UNION of selects - select_lex and unit are both inherited form select_lex_node + select_lex and unit are both inherited form st_select_lex_node neighbors are two select_lex or units on the same level All select describing structures linked with following pointers: @@ -651,13 +677,6 @@ public: ulonglong options; /* - In sql_cache we store SQL_CACHE flag as specified by user to be - able to restore SELECT statement from internal structures. - */ - enum e_sql_cache { SQL_CACHE_UNSPECIFIED, SQL_NO_CACHE, SQL_CACHE }; - e_sql_cache sql_cache; - - /* result of this query can't be cached, bit field, can be : UNCACHEABLE_DEPENDENT_GENERATED UNCACHEABLE_DEPENDENT_INJECTED @@ -667,11 +686,15 @@ public: UNCACHEABLE_PREPARE */ uint8 uncacheable; +private: enum sub_select_type linkage; +public: bool is_linkage_set() const { return linkage == UNION_TYPE || linkage == INTERSECT_TYPE || linkage == EXCEPT_TYPE; } + enum sub_select_type get_linkage() { return linkage; } + bool distinct; bool no_table_names_allowed; /* used for global order by */ static void *operator new(size_t size, MEM_ROOT *mem_root) throw () @@ -689,13 +712,33 @@ public: } inline st_select_lex_node* get_master() { return master; } + inline st_select_lex_node* get_slave() { return slave; } void include_down(st_select_lex_node *upper); void add_slave(st_select_lex_node *slave_arg); void include_neighbour(st_select_lex_node *before); + void link_chain_down(st_select_lex_node *first); + void link_neighbour(st_select_lex_node *neighbour) + { + DBUG_ASSERT(next == NULL); + DBUG_ASSERT(neighbour != NULL); + next= neighbour; + neighbour->prev= &next; + } + void cut_next() { next= NULL; } void include_standalone(st_select_lex_node *sel, st_select_lex_node **ref); void include_global(st_select_lex_node **plink); void exclude(); void exclude_from_tree(); + void exclude_from_global() + { + if (!link_prev) + return; + if (((*link_prev)= link_next)) + link_next->link_prev= link_prev; + link_next= NULL; + link_prev= NULL; + } + void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; } void move_node(st_select_lex_node *where_to_move) @@ -711,6 +754,22 @@ public: st_select_lex_node *insert_chain_before(st_select_lex_node **ptr_pos_to_insert, st_select_lex_node *end_chain_node); void move_as_slave(st_select_lex_node *new_master); + void set_linkage(enum sub_select_type l) + { + DBUG_ENTER("st_select_lex_node::set_linkage"); + DBUG_PRINT("info", ("node: %p linkage: %d->%d", this, linkage, l)); + linkage= l; + DBUG_VOID_RETURN; + } + /* + This method created for reiniting LEX in mysql_admin_table() and can be + used only if you are going remove all SELECT_LEX & units except belonger + to LEX (LEX::unit & LEX::select, for other purposes there are + SELECT_LEX_UNIT::exclude_level & SELECT_LEX_UNIT::exclude_tree. + + It is also used in parsing to detach builtin select. + */ + void cut_subtree() { slave= 0; } friend class st_select_lex_unit; friend bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); friend bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, @@ -720,6 +779,8 @@ public: friend bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *orig_table_list); friend bool TABLE_LIST::init_derived(THD *thd, bool init_view); + + friend class st_select_lex; private: void fast_exclude(); }; @@ -765,9 +826,9 @@ public: { } - TABLE *table; /* temporary table using for appending UNION results */ select_result *result; + st_select_lex *pre_last_parse; bool prepared, // prepare phase already performed for UNION (unit) optimized, // optimize phase already performed for UNION (unit) optimized_2, @@ -854,7 +915,7 @@ public: { return reinterpret_cast<st_select_lex*>(slave); } - inline void set_with_clause(With_clause *with_cl); + void set_with_clause(With_clause *with_cl); st_select_lex_unit* next_unit() { return reinterpret_cast<st_select_lex_unit*>(next); @@ -897,6 +958,18 @@ public: int save_union_explain(Explain_query *output); int save_union_explain_part2(Explain_query *output); unit_common_op common_op(); + + void reset_distinct(); + void fix_distinct(st_select_lex_unit *new_unit); + + void register_select_chain(SELECT_LEX *first_sel); + + bool set_nest_level(int new_nest_level); + bool check_parameters(SELECT_LEX *main_select); + + bool set_lock_to_the_last_select(Lex_select_lock l); + + friend class st_select_lex; }; typedef class st_select_lex_unit SELECT_LEX_UNIT; @@ -904,25 +977,42 @@ typedef Bounds_checked_array<Item*> Ref_ptr_array; /* - Structure which consists of the field and the item which - produces this field. + Structure which consists of the field and the item that + corresponds to this field. */ - -class Grouping_tmp_field :public Sql_alloc +class Field_pair :public Sql_alloc { public: - Field *tmp_field; - Item *producing_item; - Grouping_tmp_field(Field *fld, Item *item) - :tmp_field(fld), producing_item(item) {} + Field *field; + Item *corresponding_item; + Field_pair(Field *fld, Item *item) + :field(fld), corresponding_item(item) {} }; + /* SELECT_LEX - store information of parsed SELECT statment */ class st_select_lex: public st_select_lex_node { public: + /* + Currently the field first_nested is used only by parser. + It containa either a reference to the first select + of the nest of selects to which 'this' belongs to, or + in the case of priority jump it contains a reference to + the select to which the priority nest has to be attached to. + If there is no priority jump then the first select of the + nest contains the reference to itself in first_nested. + Example: + select1 union select2 intersect select + Here we have a priority jump at select2. + So select2->first_nested points to select1, + while select3->first_nested points to select2 and + select1->first_nested points to select1. + */ + st_select_lex *first_nested; + Name_resolution_context context; LEX_CSTRING db; Item *where, *having; /* WHERE & HAVING clauses */ @@ -1018,6 +1108,7 @@ public: SQL_I_List<ORDER> order_list; /* ORDER clause */ SQL_I_List<ORDER> gorder_list; Item *select_limit, *offset_limit; /* LIMIT clause parameters */ + bool is_set_query_expr_tail; /// Array of pointers to top elements of all_fields list Ref_ptr_array ref_pointer_array; @@ -1140,7 +1231,8 @@ public: nesting_map name_visibility_map; table_map with_dep; - List<Grouping_tmp_field> grouping_tmp_fields; + /* the structure to store fields that are used in the GROUP BY of this select */ + List<Field_pair> grouping_tmp_fields; /* it is for correct printing SELECT options */ thr_lock_type lock_type; @@ -1158,6 +1250,14 @@ public: void init_query(); void init_select(); st_select_lex_unit* master_unit() { return (st_select_lex_unit*) master; } + inline void set_master_unit(st_select_lex_unit *master_unit) + { + master= (st_select_lex_node *)master_unit; + } + void set_master(st_select_lex *master_arg) + { + master= master_arg; + } st_select_lex_unit* first_inner_unit() { return (st_select_lex_unit*) slave; @@ -1209,12 +1309,6 @@ public: List<Item>* get_item_list(); ulong get_table_join_options(); void set_lock_for_tables(thr_lock_type lock_type); - inline void init_order() - { - order_list.elements= 0; - order_list.first= 0; - order_list.next= &order_list.first; - } /* This method created for reiniting LEX in mysql_admin_table() and can be used only if you are going remove all SELECT_LEX & units except belonger @@ -1345,9 +1439,8 @@ public: With_element *find_table_def_in_with_clauses(TABLE_LIST *table); bool check_unrestricted_recursive(bool only_standard_compliant); bool check_subqueries_with_recursive_references(); - void collect_grouping_fields(THD *thd, ORDER *grouping_list); - void check_cond_extraction_for_grouping_fields(Item *cond, - TABLE_LIST *derived); + void collect_grouping_fields(THD *thd, ORDER *grouping_list); + void check_cond_extraction_for_grouping_fields(Item *cond); Item *build_cond_for_grouping_fields(THD *thd, Item *cond, bool no_to_clones); @@ -1373,6 +1466,11 @@ public: bool cond_pushdown_is_allowed() const { return !olap && !explicit_limit && !tvc; } + void pushdown_cond_into_where_clause(THD *thd, Item *extracted_cond, + Item **remaining_cond, + Item_transformer transformer, + uchar *arg); + private: bool m_non_agg_field_used; bool m_agg_func_used; @@ -1390,6 +1488,35 @@ public: DBUG_ASSERT(this != sel); select_n_where_fields+= sel->select_n_where_fields; } + inline void set_linkage_and_distinct(enum sub_select_type l, bool d) + { + DBUG_ENTER("SELECT_LEX::set_linkage_and_distinct"); + DBUG_PRINT("info", ("select: %p distinct %d", this, d)); + set_linkage(l); + DBUG_ASSERT(l == UNION_TYPE || + l == INTERSECT_TYPE || + l == EXCEPT_TYPE); + if (d && master_unit() && master_unit()->union_distinct != this) + master_unit()->union_distinct= this; + distinct= d; + with_all_modifier= !distinct; + DBUG_VOID_RETURN; + } + bool set_nest_level(int new_nest_level); + bool check_parameters(SELECT_LEX *main_select); + void mark_select() + { + DBUG_ENTER("st_select_lex::mark_select()"); + DBUG_PRINT("info", ("Select #%d", select_number)); + DBUG_VOID_RETURN; + } + void register_unit(SELECT_LEX_UNIT *unit, + Name_resolution_context *outer_context); + SELECT_LEX_UNIT *attach_selects_chain(SELECT_LEX *sel, + Name_resolution_context *context); + void add_statistics(SELECT_LEX_UNIT *unit); + bool make_unique_derived_name(THD *thd, LEX_CSTRING *alias); + void lex_start(LEX *plex); }; typedef class st_select_lex SELECT_LEX; @@ -2789,8 +2916,13 @@ class Query_arena_memroot; struct LEX: public Query_tables_list { SELECT_LEX_UNIT unit; /* most upper unit */ - SELECT_LEX select_lex; /* first SELECT_LEX */ + inline SELECT_LEX *first_select_lex() {return unit.first_select();} + +private: + SELECT_LEX builtin_select; /* current SELECT_LEX in parsing */ + +public: SELECT_LEX *current_select; /* list of all SELECT_LEX */ SELECT_LEX *all_selects_list; @@ -2892,6 +3024,12 @@ private: bool sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop); bool sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop); + /* + Check if Item_field and Item_ref are allowed in the current statement. + @retval false OK (fields are allowed) + @retval true ERROR (fields are not allowed). Error is raised. + */ + bool check_expr_allows_fields_or_error(THD *thd, const char *name) const; public: void parse_error(uint err_number= ER_SYNTAX_ERROR); inline bool is_arena_for_set_stmt() {return arena_for_set_stmt != 0;} @@ -2919,6 +3057,8 @@ public: required a local context, the parser pops the top-most context. */ List<Name_resolution_context> context_stack; + SELECT_LEX *select_stack[MAX_SELECT_NESTING + 1]; + uint select_stack_top; SQL_I_List<ORDER> proc_list; SQL_I_List<TABLE_LIST> auxiliary_table_list, save_list; @@ -2958,6 +3098,8 @@ public: syntax error back. */ bool expr_allows_subselect; + bool selects_allow_into; + bool selects_allow_procedure; /* A special command "PARSE_VCOL_EXPR" is defined for the parser to translate a defining expression of a virtual column into an @@ -3012,7 +3154,17 @@ public: enum enum_yes_no_unknown tx_chain, tx_release; bool safe_to_cache_query; bool subqueries, ignore; + bool next_is_main; // use "main" SELECT_LEX for nrxt allocation; + bool next_is_down; // use "main" SELECT_LEX for nrxt allocation; st_parsing_options parsing_options; + uint8 lex_options; // see OPTION_LEX_* + /* + In sql_cache we store SQL_CACHE flag as specified by user to be + able to restore SELECT statement from internal structures. + */ + enum e_sql_cache { SQL_CACHE_UNSPECIFIED, SQL_NO_CACHE, SQL_CACHE }; + e_sql_cache sql_cache; + Alter_info alter_info; /* For CREATE TABLE statement last element of table list which is not @@ -3211,20 +3363,24 @@ public: SELECT_LEX *sl; SELECT_LEX_UNIT *un; for (sl= current_select, un= sl->master_unit(); - un != &unit; - sl= sl->outer_select(), un= sl->master_unit()) + un && un != &unit; + sl= sl->outer_select(), un= (sl ? sl->master_unit() : NULL)) { - sl->uncacheable|= cause; - un->uncacheable|= cause; + sl->uncacheable|= cause; + un->uncacheable|= cause; } - select_lex.uncacheable|= cause; + if (sl) + sl->uncacheable|= cause; } + if (first_select_lex()) + first_select_lex()->uncacheable|= cause; } void set_trg_event_type_for_tables(); TABLE_LIST *unlink_first_table(bool *link_to_local); void link_first_table_back(TABLE_LIST *first, bool link_to_local); void first_lists_tables_same(); + void fix_first_select_number(); bool can_be_merged(); bool can_use_merged(); @@ -3262,14 +3418,83 @@ public: void cleanup_after_one_table_open(); - bool push_context(Name_resolution_context *context, MEM_ROOT *mem_root) + bool push_context(Name_resolution_context *context); + + void pop_context() { - return context_stack.push_front(context, mem_root); + DBUG_ENTER("LEX::pop_context"); + Name_resolution_context *context= context_stack.pop(); + DBUG_PRINT("info", ("Pop context %p Select: %p (%d)", + context, context->select_lex, + (context->select_lex ? + context->select_lex->select_number: + 0))); + DBUG_VOID_RETURN; } - void pop_context() + SELECT_LEX *select_stack_head() + { + if (likely(select_stack_top)) + return select_stack[select_stack_top - 1]; + return NULL; + } + + bool push_select(SELECT_LEX *select_lex) + { + DBUG_ENTER("LEX::push_select"); + DBUG_PRINT("info", ("Top Select was %p (%d) depth: %u pushed: %p (%d)", + select_stack_head(), + select_stack_top, + (select_stack_top ? + select_stack_head()->select_number : + 0), + select_lex, select_lex->select_number)); + if (unlikely(select_stack_top > MAX_SELECT_NESTING)) + { + my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT, MYF(0)); + DBUG_RETURN(TRUE); + } + if (push_context(&select_lex->context)) + DBUG_RETURN(TRUE); + select_stack[select_stack_top++]= select_lex; + current_select= select_lex; + DBUG_RETURN(FALSE); + } + + SELECT_LEX *pop_select() + { + DBUG_ENTER("LEX::pop_select"); + SELECT_LEX *select_lex; + if (likely(select_stack_top)) + select_lex= select_stack[--select_stack_top]; + else + select_lex= 0; + DBUG_PRINT("info", ("Top Select is %p (%d) depth: %u poped: %p (%d)", + select_stack_head(), + select_stack_top, + (select_stack_top ? + select_stack_head()->select_number : + 0), + select_lex, + (select_lex ? select_lex->select_number : 0))); + DBUG_ASSERT(select_lex); + + pop_context(); + + if (unlikely(!select_stack_top)) + { + current_select= NULL; + DBUG_PRINT("info", ("Top Select is empty")); + } + else + current_select= select_stack[select_stack_top - 1]; + + DBUG_RETURN(select_lex); + } + + SELECT_LEX *current_select_or_default() { - context_stack.pop(); + return current_select ? current_select : &builtin_select; } bool copy_db_to(LEX_CSTRING *to); @@ -3278,6 +3503,7 @@ public: { return context_stack.head(); } + /* Restore the LEX and THD in case of a parse error. */ @@ -3306,9 +3532,8 @@ public: on its top. So select_lex (as the first added) will be at the tail of the list. */ - if (&select_lex == all_selects_list && !sroutines.records) + if (first_select_lex() == all_selects_list && !sroutines.records) { - DBUG_ASSERT(!all_selects_list->next_select_in_list()); return TRUE; } return FALSE; @@ -3347,9 +3572,6 @@ public: int case_stmt_action_expr(Item* expr); int case_stmt_action_when(Item *when, bool simple); int case_stmt_action_then(); - bool add_select_to_union_list(bool is_union_distinct, - enum sub_select_type type, - bool is_top_level); bool setup_select_in_parentheses(); bool set_trigger_new_row(const LEX_CSTRING *name, Item *val); bool set_trigger_field(const LEX_CSTRING *name1, const LEX_CSTRING *name2, @@ -3474,7 +3696,12 @@ public: return create_item_qualified_asterisk(thd, &a, &b); } - Item *create_item_ident_nosp(THD *thd, Lex_ident_sys_st *name); + Item *create_item_ident_field(THD *thd, const char *db, const char *table, + const Lex_ident_sys_st *name); + Item *create_item_ident_nosp(THD *thd, Lex_ident_sys_st *name) + { + return create_item_ident_field(thd, NullS, NullS, name); + } Item *create_item_ident_sp(THD *thd, Lex_ident_sys_st *name, const char *start, const char *end); Item *create_item_ident(THD *thd, Lex_ident_cli_st *cname) @@ -3612,6 +3839,10 @@ public: const Lex_ident_cli_st *var_name, const Lex_ident_cli_st *field_name); + Item *create_item_query_expression(THD *thd, + const char *tok_start, + st_select_lex_unit *unit); + Item *make_item_func_replace(THD *thd, Item *org, Item *find, Item *replace); Item *make_item_func_substr(THD *thd, Item *a, Item *b, Item *c); Item *make_item_func_substr(THD *thd, Item *a, Item *b); @@ -3806,6 +4037,17 @@ public: sp_for_loop_intrange_finalize(thd, loop); } bool sp_for_loop_outer_block_finalize(THD *thd, const Lex_for_loop_st &loop); + + /* + Make an Item when an identifier is found in the FOR loop bounds: + FOR rec IN cursor + FOR rec IN var1 .. var2 + FOR rec IN row1.field1 .. xxx + */ + Item *create_item_for_loop_bound(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b, + const LEX_CSTRING *c); /* End of FOR LOOP methods */ bool add_signal_statement(THD *thd, const class sp_condition_value *value); @@ -3928,7 +4170,7 @@ public: bool if_exists() const { return create_info.if_exists(); } SELECT_LEX *exclude_last_select(); - bool add_unit_in_brackets(SELECT_LEX *nselect); + SELECT_LEX *exclude_not_first_select(SELECT_LEX *exclude); void check_automatic_up(enum sub_select_type type); bool create_or_alter_view_finalize(THD *thd, Table_ident *table_ident); bool add_alter_view(THD *thd, uint16 algorithm, enum_view_suid suid, @@ -3936,7 +4178,6 @@ public: bool add_create_view(THD *thd, DDL_options_st ddl, uint16 algorithm, enum_view_suid suid, Table_ident *table_ident); - bool add_grant_command(THD *thd, enum_sql_command sql_command_arg, stored_procedure_type type_arg); @@ -3955,7 +4196,7 @@ public: */ bool check_simple_select(const LEX_CSTRING *option) { - if (current_select != &select_lex) + if (current_select != &builtin_select) { char command[80]; strmake(command, option->str, MY_MIN(option->length, sizeof(command)-1)); @@ -3973,6 +4214,63 @@ public: } bool tvc_finalize(); bool tvc_finalize_derived(); + + bool make_select_in_brackets(SELECT_LEX* dummy_select, + SELECT_LEX *nselect, bool automatic); + + SELECT_LEX_UNIT *alloc_unit(); + SELECT_LEX *alloc_select(bool is_select); + SELECT_LEX_UNIT *create_unit(SELECT_LEX*); + SELECT_LEX *wrap_unit_into_derived(SELECT_LEX_UNIT *unit); + SELECT_LEX *wrap_select_chain_into_derived(SELECT_LEX *sel); + bool main_select_push(); + bool insert_select_hack(SELECT_LEX *sel); + SELECT_LEX *create_priority_nest(SELECT_LEX *first_in_nest); + + void set_main_unit(st_select_lex_unit *u) + { + unit.options= u->options; + unit.uncacheable= u->uncacheable; + unit.register_select_chain(u->first_select()); + unit.first_select()->options|= builtin_select.options; + unit.fake_select_lex= u->fake_select_lex; + unit.union_distinct= u->union_distinct; + unit.set_with_clause(u->with_clause); + builtin_select.exclude_from_global(); + } + bool check_main_unit_semantics(); + + // reaction on different parsed parts (bodies are in sql_yacc.yy) + bool parsed_unit_in_brackets(SELECT_LEX_UNIT *unit); + SELECT_LEX *parsed_select(SELECT_LEX *sel, Lex_order_limit_lock * l); + SELECT_LEX *parsed_unit_in_brackets_tail(SELECT_LEX_UNIT *unit, + Lex_order_limit_lock * l); + SELECT_LEX *parsed_select_in_brackets(SELECT_LEX *sel, + Lex_order_limit_lock * l); + SELECT_LEX_UNIT *parsed_select_expr_start(SELECT_LEX *s1, SELECT_LEX *s2, + enum sub_select_type unit_type, + bool distinct); + SELECT_LEX_UNIT *parsed_select_expr_cont(SELECT_LEX_UNIT *unit, + SELECT_LEX *s2, + enum sub_select_type unit_type, + bool distinct, bool oracle); + SELECT_LEX_UNIT *parsed_body_select(SELECT_LEX *sel, + Lex_order_limit_lock * l); + bool parsed_body_unit(SELECT_LEX_UNIT *unit); + SELECT_LEX_UNIT *parsed_body_unit_tail(SELECT_LEX_UNIT *unit, + Lex_order_limit_lock * l); + SELECT_LEX *parsed_subselect(SELECT_LEX_UNIT *unit, char *place); + bool parsed_insert_select(SELECT_LEX *firs_select); + bool parsed_TVC_start(); + SELECT_LEX *parsed_TVC_end(); + TABLE_LIST *parsed_derived_select(SELECT_LEX *sel, int for_system_time, + LEX_CSTRING *alias); + TABLE_LIST *parsed_derived_unit(SELECT_LEX_UNIT *unit, + int for_system_time, + LEX_CSTRING *alias); + bool parsed_create_view(SELECT_LEX_UNIT *unit, int check); + bool select_finalize(st_select_lex_unit *expr); + void relink_hack(st_select_lex *select_lex); }; diff --git a/sql/sql_list.h b/sql/sql_list.h index 39a1c3375e0..27827b42be5 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -611,7 +611,7 @@ struct ilink struct ilink **prev,*next; static void *operator new(size_t size) throw () { - return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE | ME_FATALERROR)); + return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE | ME_FATAL)); } static void operator delete(void* ptr_arg, size_t) { diff --git a/sql/sql_load.cc b/sql/sql_load.cc index ddb5029c78a..dd6e723c953 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -390,10 +390,13 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list, if (mysql_handle_single_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT) || mysql_handle_single_derived(thd->lex, table_list, DT_PREPARE)) DBUG_RETURN(TRUE); - if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, + if (setup_tables_and_check_access(thd, + &thd->lex->first_select_lex()->context, + &thd->lex->first_select_lex()-> + top_join_list, table_list, - thd->lex->select_lex.leaf_tables, FALSE, + thd->lex->first_select_lex()->leaf_tables, + FALSE, INSERT_ACL | UPDATE_ACL, INSERT_ACL | UPDATE_ACL, FALSE)) DBUG_RETURN(-1); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d6473d58821..1f9cd305847 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -109,6 +109,7 @@ #include "../storage/maria/ha_maria.h" #endif +#include "wsrep.h" #include "wsrep_mysqld.h" #include "wsrep_thd.h" @@ -2008,10 +2009,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, Init TABLE_LIST members necessary when the undelrying table is view. */ - table_list.select_lex= &(thd->lex->select_lex); + table_list.select_lex= thd->lex->first_select_lex(); thd->lex-> - select_lex.table_list.link_in_list(&table_list, - &table_list.next_local); + first_select_lex()->table_list.link_in_list(&table_list, + &table_list.next_local); thd->lex->add_to_query_tables(&table_list); if (is_infoschema_db(&table_list.db)) @@ -2575,23 +2576,24 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, DBUG_RETURN(1); #else { - if (lex->select_lex.db.str == NULL && - lex->copy_db_to(&lex->select_lex.db)) + if (lex->first_select_lex()->db.str == NULL && + lex->copy_db_to(&lex->first_select_lex()->db)) { DBUG_RETURN(1); } schema_select_lex= new (thd->mem_root) SELECT_LEX(); schema_select_lex->table_list.first= NULL; if (lower_case_table_names == 1) - lex->select_lex.db.str= thd->strdup(lex->select_lex.db.str); - schema_select_lex->db= lex->select_lex.db; + lex->first_select_lex()->db.str= + thd->strdup(lex->first_select_lex()->db.str); + schema_select_lex->db= lex->first_select_lex()->db; /* check_db_name() may change db.str if lower_case_table_names == 1, but that's ok as the db is allocted above in this case. */ - if (check_db_name((LEX_STRING*) &lex->select_lex.db)) + if (check_db_name((LEX_STRING*) &lex->first_select_lex()->db)) { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->select_lex.db.str); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->first_select_lex()->db.str); DBUG_RETURN(1); } break; @@ -2630,7 +2632,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, default: break; } - + if (schema_select_lex) + schema_select_lex->set_master_unit(&lex->unit); SELECT_LEX *select_lex= lex->current_select; if (make_schema_select(thd, select_lex, get_schema_table(schema_table_idx))) DBUG_RETURN(1); @@ -3008,7 +3011,7 @@ static int mysql_create_routine(THD *thd, LEX *lex) if (sp_process_definer(thd)) return true; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!lex->sphead->m_handler->sp_create_routine(thd, lex->sphead)) { #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -3077,7 +3080,9 @@ static int mysql_create_routine(THD *thd, LEX *lex) #endif return false; } -WSREP_ERROR_LABEL: +#ifdef WITH_WSREP +wsrep_error_label: +#endif return true; } @@ -3225,7 +3230,7 @@ mysql_execute_command(THD *thd) int up_result= 0; LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); /* first table of first SELECT_LEX */ TABLE_LIST *first_table= select_lex->table_list.first; /* list of all tables in query */ @@ -3264,6 +3269,7 @@ mysql_execute_command(THD *thd) DBUG_ASSERT(first_table == all_tables && first_table != 0); */ lex->first_lists_tables_same(); + lex->fix_first_select_number(); /* should be assigned after making first tables same */ all_tables= lex->query_tables; /* set context for commands which do not use setup_tables */ @@ -6296,8 +6302,10 @@ end_with_restore_list: goto finish; error: -WSREP_ERROR_LABEL: - res= TRUE; +#ifdef WITH_WSREP +wsrep_error_label: +#endif + res= true; finish: @@ -7520,7 +7528,7 @@ bool check_stack_overrun(THD *thd, long margin, if (ebuff) { my_snprintf(ebuff, MYSQL_ERRMSG_SIZE, ER_THD(thd, ER_STACK_OVERRUN_NEED_MORE), stack_used, my_thread_stack_size, margin); - my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR)); + my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATAL)); delete [] ebuff; } return 1; @@ -7590,16 +7598,21 @@ void THD::reset_for_next_command(bool do_clear_error) DBUG_ASSERT(!in_sub_stmt); if (likely(do_clear_error)) + { clear_error(1); - + /* + The following variable can't be reset in clear_error() as + clear_error() is called during auto_repair of table + */ + error_printed_to_log= 0; + } free_list= 0; /* We also assign stmt_lex in lex_start(), but during bootstrap this code is executed first. */ DBUG_ASSERT(lex == &main_lex); - main_lex.stmt_lex= &main_lex; main_lex.current_select_number= 1; - DBUG_PRINT("info", ("Lex and stmt_lex: %p", &main_lex)); + main_lex.stmt_lex= &main_lex; main_lex.current_select_number= 0; /* Those two lines below are theoretically unneeded as THD::cleanup_after_query() should take care of this already. @@ -7685,11 +7698,7 @@ mysql_init_select(LEX *lex) SELECT_LEX *select_lex= lex->current_select; select_lex->init_select(); lex->wild= 0; - if (select_lex == &lex->select_lex) - { - DBUG_ASSERT(lex->result == 0); - lex->exchange= 0; - } + lex->exchange= 0; } @@ -7710,6 +7719,7 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex) { THD *thd= lex->thd; bool new_select= select_lex == NULL; + int old_nest_level= lex->current_select->nest_level; DBUG_ENTER("mysql_new_select"); if (new_select) @@ -7721,27 +7731,19 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex) select_lex->init_query(); select_lex->init_select(); } - lex->nest_level++; - if (lex->nest_level > (int) MAX_SELECT_NESTING) - { - my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT, MYF(0)); - DBUG_RETURN(1); - } - select_lex->nest_level= lex->nest_level; select_lex->nest_level_base= &thd->lex->unit; if (move_down) { + lex->nest_level++; + if (select_lex->set_nest_level(old_nest_level + 1)) + DBUG_RETURN(1); SELECT_LEX_UNIT *unit; lex->subqueries= TRUE; /* first select_lex of subselect or derived table */ - if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT())) + if (!(unit= lex->alloc_unit())) DBUG_RETURN(1); - unit->init_query(); - unit->thd= thd; unit->include_down(lex->current_select); - unit->link_next= 0; - unit->link_prev= 0; unit->return_to= lex->current_select; select_lex->include_down(unit); /* @@ -7775,15 +7777,13 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex) "SELECT ... PROCEDURE ANALYSE()"); DBUG_RETURN(TRUE); } - // SELECT 1 FROM t1 ORDER BY 1 UNION SELECT 1 FROM t1 -- not possible - DBUG_ASSERT(!lex->current_select->order_list.first || - lex->current_select->braces); - // SELECT 1 FROM t1 LIMIT 1 UNION SELECT 1 FROM t1; -- not possible - DBUG_ASSERT(!lex->current_select->explicit_limit || - lex->current_select->braces); + SELECT_LEX_NODE *save_slave= select_lex->slave; select_lex->include_neighbour(lex->current_select); - SELECT_LEX_UNIT *unit= select_lex->master_unit(); + select_lex->slave= save_slave; + SELECT_LEX_UNIT *unit= select_lex->master_unit(); + if (select_lex->set_nest_level(old_nest_level)) + DBUG_RETURN(1); if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd)) DBUG_RETURN(1); select_lex->context.outer_context= @@ -7839,9 +7839,10 @@ void mysql_init_multi_delete(LEX *lex) { lex->sql_command= SQLCOM_DELETE_MULTI; mysql_init_select(lex); - lex->select_lex.select_limit= 0; + lex->first_select_lex()->select_limit= 0; lex->unit.select_limit_cnt= HA_POS_ERROR; - lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list); + lex->first_select_lex()->table_list. + save_and_clear(&lex->auxiliary_table_list); lex->query_tables= 0; lex->query_tables_last= &lex->query_tables; } @@ -8157,7 +8158,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length) thd->reset_for_next_command(); if (!parse_sql(thd, & parser_state, NULL, true) && - all_tables_not_ok(thd, lex->select_lex.table_list.first)) + all_tables_not_ok(thd, lex->first_select_lex()->table_list.first)) error= 1; /* Ignore question */ thd->end_statement(); } @@ -8239,6 +8240,10 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, LEX_CSTRING alias_str; LEX *lex= thd->lex; DBUG_ENTER("add_table_to_list"); + DBUG_PRINT("enter", ("Table '%s' (%p) Select %p (%u)", + (alias ? alias->str : table->table.str), + table, + this, select_number)); if (unlikely(!table)) DBUG_RETURN(0); // End of memory @@ -8332,7 +8337,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->schema_table_name= ptr->table_name; ptr->schema_table= schema_table; } - ptr->select_lex= lex->current_select; + ptr->select_lex= this; /* We can't cache internal temporary tables between prepares as the table may be deleted before next exection. @@ -8439,8 +8444,6 @@ bool st_select_lex::init_nested_join(THD *thd) nested_join= ptr->nested_join= ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST)))); - if (unlikely(join_list->push_front(ptr, thd->mem_root))) - DBUG_RETURN(1); ptr->embedding= embedding; ptr->join_list= join_list; ptr->alias.str="(nested_join)"; @@ -8548,7 +8551,6 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) ptr->join_using_fields= prev_join_using; } } - join_list->push_front(ptr, thd->mem_root); nested_join->used_tables= nested_join->not_null_tables= (table_map) 0; DBUG_RETURN(ptr); } @@ -8740,7 +8742,7 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg) { SELECT_LEX *first_sl= first_select(); - DBUG_ENTER("add_fake_select_lex"); + DBUG_ENTER("st_select_lex_unit::add_fake_select_lex"); DBUG_ASSERT(!fake_select_lex); if (!(fake_select_lex= new (thd_arg->mem_root) SELECT_LEX())) @@ -8750,16 +8752,19 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg) fake_select_lex->select_number= INT_MAX; fake_select_lex->parent_lex= thd_arg->lex; /* Used in init_query. */ fake_select_lex->make_empty_select(); - fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE; + fake_select_lex->set_linkage(GLOBAL_OPTIONS_TYPE); fake_select_lex->select_limit= 0; + fake_select_lex->no_table_names_allowed= 1; + fake_select_lex->context.outer_context=first_sl->context.outer_context; /* allow item list resolving in fake select for ORDER BY */ fake_select_lex->context.resolve_in_select_list= TRUE; fake_select_lex->context.select_lex= fake_select_lex; fake_select_lex->nest_level_base= first_select()->nest_level_base; - fake_select_lex->nest_level=first_select()->nest_level; + if (fake_select_lex->set_nest_level(first_select()->nest_level)) + DBUG_RETURN(1); if (!is_unit_op()) { @@ -8772,7 +8777,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg) fake_select_lex->no_table_names_allowed= 1; thd_arg->lex->current_select= fake_select_lex; } - thd_arg->lex->pop_context(); + //thd_arg->lex->pop_context("add fake"); DBUG_RETURN(0); } @@ -8808,7 +8813,7 @@ push_new_name_resolution_context(THD *thd, left_op->first_leaf_for_name_resolution(); on_context->last_name_resolution_table= right_op->last_leaf_for_name_resolution(); - return thd->lex->push_context(on_context, thd->mem_root); + return thd->lex->push_context(on_context); } @@ -9237,7 +9242,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) { TABLE_LIST *table; LEX *lex= thd->lex; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); DBUG_ENTER("multi_update_precheck"); if (select_lex->item_list.elements != lex->value_list.elements) @@ -9273,7 +9278,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) /* Is there tables of subqueries? */ - if (&lex->select_lex != lex->all_selects_list) + if (lex->first_select_lex() != lex->all_selects_list) { DBUG_PRINT("info",("Checking sub query list")); for (table= tables; table; table= table->next_global) @@ -9307,7 +9312,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) { - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last; DBUG_ENTER("multi_delete_precheck"); @@ -9424,7 +9429,7 @@ static TABLE_LIST *multi_delete_table_match(LEX *lex, TABLE_LIST *tbl, bool multi_delete_set_locks_and_link_aux_tables(LEX *lex) { - TABLE_LIST *tables= lex->select_lex.table_list.first; + TABLE_LIST *tables= lex->first_select_lex()->table_list.first; TABLE_LIST *target_tbl; DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables"); @@ -9466,7 +9471,8 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex) bool update_precheck(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("update_precheck"); - if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements) + if (thd->lex->first_select_lex()->item_list.elements != + thd->lex->value_list.elements) { my_message(ER_WRONG_VALUE_COUNT, ER_THD(thd, ER_WRONG_VALUE_COUNT), MYF(0)); DBUG_RETURN(TRUE); @@ -9557,7 +9563,7 @@ void create_table_set_open_action_and_adjust_tables(LEX *lex) else create_table->open_type= OT_BASE_ONLY; - if (!lex->select_lex.item_list.elements) + if (!lex->first_select_lex()->item_list.elements) { /* Avoid opening and locking target table for ordinary CREATE TABLE @@ -9588,7 +9594,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table) { LEX *lex= thd->lex; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); ulong want_priv; bool error= TRUE; // Error message is given DBUG_ENTER("create_table_precheck"); @@ -9730,8 +9736,9 @@ Item *negate_expression(THD *thd, Item *expr) { /* it is NOT(NOT( ... )) */ Item *arg= ((Item_func *) expr)->arguments()[0]; + const Type_handler *fh= arg->fixed_type_handler(); enum_parsing_place place= thd->lex->current_select->parsing_place; - if (arg->is_bool_type() || place == IN_WHERE || place == IN_HAVING) + if ((fh && fh->is_bool_type()) || place == IN_WHERE || place == IN_HAVING) return arg; /* if it is not boolean function then we have to emulate value of @@ -10099,6 +10106,9 @@ bool parse_sql(THD *thd, Parser_state *parser_state, ((thd->variables.sql_mode & MODE_ORACLE) ? ORAparse(thd) : MYSQLparse(thd)) != 0; + DBUG_ASSERT(opt_bootstrap || mysql_parse_status || + thd->lex->select_stack_top == 0); + thd->lex->current_select= thd->lex->first_select_lex(); /* Check that if MYSQLparse() failed either thd->is_error() is set, or an diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 151b94d1187..95d14d9ab0d 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -525,15 +525,10 @@ static bool create_full_part_field_array(THD *thd, TABLE *table, full_part_field_array may be NULL if storage engine supports native partitioning. */ - table->vcol_set= table->read_set= &part_info->full_part_field_set; + table->read_set= &part_info->full_part_field_set; if ((ptr= part_info->full_part_field_array)) for (; *ptr; ptr++) - { - if ((*ptr)->vcol_info) - table->mark_virtual_col(*ptr); - else - bitmap_fast_test_and_set(table->read_set, (*ptr)->field_index); - } + table->mark_column_with_deps(*ptr); table->default_column_bitmaps(); end: @@ -835,7 +830,8 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, goto end; table->get_fields_in_item_tree= true; - func_expr->walk(&Item::change_context_processor, 0, &lex.select_lex.context); + func_expr->walk(&Item::change_context_processor, 0, + &lex.first_select_lex()->context); thd->where= "partition function"; /* In execution we must avoid the use of thd->change_item_tree since @@ -1563,7 +1559,7 @@ static bool check_vers_constants(THD *thd, partition_info *part_info) my_tz_OFFSET0->gmt_sec_to_TIME(<ime, vers_info->interval.start); while ((el= it++)->id < hist_parts) { - if (date_add_interval(<ime, vers_info->interval.type, + if (date_add_interval(thd, <ime, vers_info->interval.type, vers_info->interval.step)) goto err; uint error= 0; @@ -2656,7 +2652,7 @@ char *generate_partition_syntax(THD *thd, partition_info *part_info, default: DBUG_ASSERT(0); /* We really shouldn't get here, no use in continuing from here */ - my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); + my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL)); DBUG_RETURN(NULL); } if (part_info->part_type == VERSIONING_PARTITION) @@ -6003,7 +5999,7 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) lpt->pack_frm_data, lpt->pack_frm_len)))) { - file->print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATALERROR)); + file->print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATAL)); } if (mysql_trans_commit_alter_copy_data(thd)) @@ -8221,7 +8217,7 @@ static int get_part_iter_for_interval_via_mapping(partition_info *part_info, field->type() == MYSQL_TYPE_DATETIME)) { /* Monotonic, but return NULL for dates with zeros in month/day. */ - zero_in_start_date= field->get_date(&start_date, 0); + zero_in_start_date= field->get_date(&start_date, date_mode_t(0)); DBUG_PRINT("info", ("zero start %u %04d-%02d-%02d", zero_in_start_date, start_date.year, start_date.month, start_date.day)); @@ -8245,7 +8241,7 @@ static int get_part_iter_for_interval_via_mapping(partition_info *part_info, !part_info->part_expr->null_value) { MYSQL_TIME end_date; - bool zero_in_end_date= field->get_date(&end_date, 0); + bool zero_in_end_date= field->get_date(&end_date, date_mode_t(0)); /* This is an optimization for TO_DAYS()/TO_SECONDS() to avoid scanning the NULL partition for ranges that cannot include a date with 0 as diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index 2d3c640b758..71ab7477391 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -51,7 +51,7 @@ bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd) /* Moved from mysql_execute_command */ LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); /* first table of first SELECT_LEX */ TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first; /* @@ -743,7 +743,7 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd) int error; ha_partition *partition; ulong timeout= thd->variables.lock_wait_timeout; - TABLE_LIST *first_table= thd->lex->select_lex.table_list.first; + TABLE_LIST *first_table= thd->lex->first_select_lex()->table_list.first; Alter_info *alter_info= &thd->lex->alter_info; uint table_counter, i; List<String> partition_names_list; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index b2ceb1627a1..d448b7b9e02 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -280,6 +280,7 @@ struct st_mysql_sys_var MYSQL_PLUGIN_VAR_HEADER; }; +enum install_status { INSTALL_GOOD, INSTALL_FAIL_WARN_OK, INSTALL_FAIL_NOT_OK }; /* sys_var class for access to all plugin variables visible to the user */ @@ -1077,7 +1078,7 @@ static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin) NOTE Requires that a write-lock is held on LOCK_system_variables_hash */ -static bool plugin_add(MEM_ROOT *tmp_root, +static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists, const LEX_CSTRING *name, LEX_CSTRING *dl, myf MyFlags) { struct st_plugin_int tmp, *maybe_dupe; @@ -1088,14 +1089,16 @@ static bool plugin_add(MEM_ROOT *tmp_root, if (name->str && plugin_find_internal(name, MYSQL_ANY_PLUGIN)) { + if (if_not_exists) + MyFlags|= ME_NOTE; my_error(ER_PLUGIN_INSTALLED, MyFlags, name->str); - DBUG_RETURN(TRUE); + DBUG_RETURN(if_not_exists ? INSTALL_FAIL_WARN_OK : INSTALL_FAIL_NOT_OK); } /* Clear the whole struct to catch future extensions. */ bzero((char*) &tmp, sizeof(tmp)); fix_dl_name(tmp_root, dl); if (! (tmp.plugin_dl= plugin_dl_add(dl, MyFlags))) - DBUG_RETURN(TRUE); + DBUG_RETURN(INSTALL_FAIL_NOT_OK); /* Find plugin by name */ for (plugin= tmp.plugin_dl->plugins; plugin->info; plugin++) { @@ -1121,7 +1124,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, if (plugin->name != maybe_dupe->plugin->name) { my_error(ER_UDF_EXISTS, MyFlags, plugin->name); - DBUG_RETURN(TRUE); + DBUG_RETURN(INSTALL_FAIL_NOT_OK); } dupes++; continue; // already installed @@ -1173,7 +1176,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, init_alloc_root(&tmp_plugin_ptr->mem_root, "plugin", 4096, 4096, MYF(0)); if (name->str) - DBUG_RETURN(FALSE); // all done + DBUG_RETURN(INSTALL_GOOD); // all done oks++; tmp.plugin_dl->ref_count++; @@ -1191,7 +1194,9 @@ err: my_error(ER_CANT_FIND_DL_ENTRY, MyFlags, name->str); plugin_dl_del(tmp.plugin_dl); - DBUG_RETURN(errs > 0 || oks + dupes == 0); + if (errs > 0 || oks + dupes == 0) + DBUG_RETURN(INSTALL_FAIL_NOT_OK); + DBUG_RETURN(INSTALL_GOOD); } static void plugin_variables_deinit(struct st_plugin_int *plugin) @@ -1847,7 +1852,7 @@ static void plugin_load(MEM_ROOT *tmp_root) the mutex here to satisfy the assert */ mysql_mutex_lock(&LOCK_plugin); - plugin_add(tmp_root, &name, &dl, MYF(ME_ERROR_LOG)); + plugin_add(tmp_root, false, &name, &dl, MYF(ME_ERROR_LOG)); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); mysql_mutex_unlock(&LOCK_plugin); } @@ -1870,7 +1875,7 @@ end: static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) { char buffer[FN_REFLEN]; - LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name; + LEX_CSTRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name; char *p= buffer; DBUG_ENTER("plugin_load_list"); while (list) @@ -1889,7 +1894,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) #ifndef __WIN__ case ':': /* can't use this as delimiter as it may be drive letter */ #endif - str->str[str->length]= '\0'; + p[-1]= 0; if (str == &name) // load all plugins in named module { if (!name.length) @@ -1902,16 +1907,16 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) mysql_mutex_lock(&LOCK_plugin); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); name.str= 0; // load everything - if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl, - MYF(ME_ERROR_LOG))) + if (plugin_add(tmp_root, false, &name, &dl, + MYF(ME_ERROR_LOG)) != INSTALL_GOOD) goto error; } else { free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); mysql_mutex_lock(&LOCK_plugin); - if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl, - MYF(ME_ERROR_LOG))) + if (plugin_add(tmp_root, false, &name, &dl, + MYF(ME_ERROR_LOG)) != INSTALL_GOOD) goto error; } mysql_mutex_unlock(&LOCK_plugin); @@ -1923,7 +1928,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) case '#': if (str == &name) { - name.str[name.length]= '\0'; + p[-1]= 0; str= &dl; str->str= p; continue; @@ -2146,7 +2151,7 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, TABLE_LIST tables; TABLE *table; LEX_CSTRING dl= *dl_arg; - bool error; + enum install_status error; int argc=orig_argc; char **argv=orig_argv; unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] = @@ -2194,12 +2199,14 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, mysql_audit_acquire_plugins(thd, event_class_mask); mysql_mutex_lock(&LOCK_plugin); - error= plugin_add(thd->mem_root, name, &dl, MYF(0)); - if (unlikely(error)) + error= plugin_add(thd->mem_root, thd->lex->create_info.if_not_exists(), + name, &dl, MYF(0)); + if (unlikely(error != INSTALL_GOOD)) goto err; if (name->str) - error= finalize_install(thd, table, name, &argc, argv); + error= finalize_install(thd, table, name, &argc, argv) + ? INSTALL_FAIL_NOT_OK : INSTALL_GOOD; else { st_plugin_dl *plugin_dl= plugin_dl_find(&dl); @@ -2207,11 +2214,12 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, for (plugin= plugin_dl->plugins; plugin->info; plugin++) { LEX_CSTRING str= { plugin->name, strlen(plugin->name) }; - error|= finalize_install(thd, table, &str, &argc, argv); + if (finalize_install(thd, table, &str, &argc, argv)) + error= INSTALL_FAIL_NOT_OK; } } - if (unlikely(error)) + if (unlikely(error != INSTALL_GOOD)) { reap_needed= true; reap_plugins(); @@ -2220,10 +2228,11 @@ err: mysql_mutex_unlock(&LOCK_plugin); if (argv) free_defaults(argv); - DBUG_RETURN(error); - -WSREP_ERROR_LABEL: - DBUG_RETURN(TRUE); + DBUG_RETURN(error == INSTALL_FAIL_NOT_OK); +#ifdef WITH_WSREP +wsrep_error_label: + DBUG_RETURN(true); +#endif } @@ -2235,8 +2244,9 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name) if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)) || plugin->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DYING)) { - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str); - return 1; + myf MyFlags= thd->lex->if_exists() ? ME_NOTE : 0; + my_error(ER_SP_DOES_NOT_EXIST, MyFlags, "PLUGIN", name->str); + return !MyFlags; } if (!plugin->plugin_dl) { @@ -2299,7 +2309,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, if (!opt_noacl && check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) @@ -2358,17 +2368,19 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, } else { - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SONAME", dl.str); - error= true; + myf MyFlags= thd->lex->if_exists() ? ME_NOTE : 0; + my_error(ER_SP_DOES_NOT_EXIST, MyFlags, "SONAME", dl.str); + error|= !MyFlags; } } reap_plugins(); mysql_mutex_unlock(&LOCK_plugin); DBUG_RETURN(error); - -WSREP_ERROR_LABEL: - DBUG_RETURN(TRUE); +#ifdef WITH_WSREP +wsrep_error_label: + DBUG_RETURN(true); +#endif } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index c4871bdcc80..09d31470a00 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1354,7 +1354,7 @@ static int mysql_test_update(Prepared_statement *stmt, THD *thd= stmt->thd; uint table_count= 0; TABLE_LIST *update_source_table; - SELECT_LEX *select= &stmt->lex->select_lex; + SELECT_LEX *select= stmt->lex->first_select_lex(); #ifndef NO_EMBEDDED_ACCESS_CHECKS uint want_privilege; #endif @@ -1410,10 +1410,10 @@ static int mysql_test_update(Prepared_statement *stmt, table_list->table->grant.want_privilege= want_privilege; table_list->register_want_access(want_privilege); #endif - thd->lex->select_lex.no_wrap_view_item= TRUE; + thd->lex->first_select_lex()->no_wrap_view_item= TRUE; res= setup_fields(thd, Ref_ptr_array(), select->item_list, MARK_COLUMNS_READ, 0, NULL, 0); - thd->lex->select_lex.no_wrap_view_item= FALSE; + thd->lex->first_select_lex()->no_wrap_view_item= FALSE; if (res) goto error; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -1478,10 +1478,10 @@ static bool mysql_test_delete(Prepared_statement *stmt, goto error; } - DBUG_RETURN(mysql_prepare_delete(thd, table_list, - lex->select_lex.with_wild, - lex->select_lex.item_list, - &lex->select_lex.where, + DBUG_RETURN(mysql_prepare_delete(thd, table_list, + lex->first_select_lex()->with_wild, + lex->first_select_lex()->item_list, + &lex->first_select_lex()->where, &delete_while_scanning)); error: DBUG_RETURN(TRUE); @@ -1513,7 +1513,7 @@ static int mysql_test_select(Prepared_statement *stmt, SELECT_LEX_UNIT *unit= &lex->unit; DBUG_ENTER("mysql_test_select"); - lex->select_lex.context.resolve_in_select_list= TRUE; + lex->first_select_lex()->context.resolve_in_select_list= TRUE; ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL; if (tables) @@ -1526,7 +1526,7 @@ static int mysql_test_select(Prepared_statement *stmt, if (!lex->result && !(lex->result= new (stmt->mem_root) select_send(thd))) { - my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), + my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), static_cast<int>(sizeof(select_send))); goto error; } @@ -1547,7 +1547,7 @@ static int mysql_test_select(Prepared_statement *stmt, if (!lex->describe && !thd->lex->analyze_stmt && !stmt->is_sql_prepare()) { /* Make copy of item list, as change_columns may change it */ - List<Item> fields(lex->select_lex.item_list); + List<Item> fields(lex->first_select_lex()->item_list); /* Change columns if a procedure like analyse() */ if (unit->last_procedure && unit->last_procedure->change_columns(thd, fields)) @@ -1705,7 +1705,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt, THD *thd= stmt->thd; LEX *lex= stmt->lex; - lex->select_lex.context.resolve_in_select_list= TRUE; + lex->first_select_lex()->context.resolve_in_select_list= TRUE; if (specific_prepare && (*specific_prepare)(thd)) DBUG_RETURN(TRUE); @@ -1773,7 +1773,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt) DBUG_ENTER("mysql_test_create_table"); THD *thd= stmt->thd; LEX *lex= stmt->lex; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); bool res= FALSE; bool link_to_local; TABLE_LIST *create_table= lex->query_tables; @@ -2093,11 +2093,11 @@ static bool mysql_test_multidelete(Prepared_statement *stmt, { THD *thd= stmt->thd; - thd->lex->current_select= &thd->lex->select_lex; + thd->lex->current_select= thd->lex->first_select_lex(); if (add_item_to_list(thd, new (thd->mem_root) Item_null(thd))) { - my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 0); + my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 0); goto error; } @@ -2132,13 +2132,14 @@ error: static int mysql_insert_select_prepare_tester(THD *thd) { - SELECT_LEX *first_select= &thd->lex->select_lex; + SELECT_LEX *first_select= thd->lex->first_select_lex(); TABLE_LIST *second_table= first_select->table_list.first->next_local; /* Skip first table, which is the table we are inserting in */ first_select->table_list.first= second_table; - thd->lex->select_lex.context.table_list= - thd->lex->select_lex.context.first_name_resolution_table= second_table; + thd->lex->first_select_lex()->context.table_list= + thd->lex->first_select_lex()->context.first_name_resolution_table= + second_table; return mysql_insert_select_prepare(thd); } @@ -2173,7 +2174,7 @@ static bool mysql_test_insert_select(Prepared_statement *stmt, return 1; /* store it, because mysql_insert_select_prepare_tester change it */ - first_local_table= lex->select_lex.table_list.first; + first_local_table= lex->first_select_lex()->table_list.first; DBUG_ASSERT(first_local_table != 0); res= @@ -2181,7 +2182,7 @@ static bool mysql_test_insert_select(Prepared_statement *stmt, &mysql_insert_select_prepare_tester, OPTION_SETUP_TABLES_DONE); /* revert changes made by mysql_insert_select_prepare_tester */ - lex->select_lex.table_list.first= first_local_table; + lex->first_select_lex()->table_list.first= first_local_table; return res; } @@ -2207,7 +2208,7 @@ static int mysql_test_handler_read(Prepared_statement *stmt, SQL_HANDLER *ha_table; DBUG_ENTER("mysql_test_handler_read"); - lex->select_lex.context.resolve_in_select_list= TRUE; + lex->first_select_lex()->context.resolve_in_select_list= TRUE; /* We don't have to test for permissions as this is already done during @@ -2217,7 +2218,7 @@ static int mysql_test_handler_read(Prepared_statement *stmt, lex->ident.str, lex->insert_list, lex->ha_rkey_mode, - lex->select_lex.where))) + lex->first_select_lex()->where))) DBUG_RETURN(1); if (!stmt->is_sql_prepare()) @@ -2256,7 +2257,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) { THD *thd= stmt->thd; LEX *lex= stmt->lex; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); TABLE_LIST *tables; enum enum_sql_command sql_command= lex->sql_command; int res= 0; @@ -2265,10 +2266,11 @@ static bool check_prepared_statement(Prepared_statement *stmt) sql_command, stmt->param_count)); lex->first_lists_tables_same(); + lex->fix_first_select_number(); tables= lex->query_tables; /* set context for commands which do not use setup_tables */ - lex->select_lex.context.resolve_in_table_list_only(select_lex-> + lex->first_select_lex()->context.resolve_in_table_list_only(select_lex-> get_table_list()); /* Reset warning count for each query that uses tables */ @@ -3033,7 +3035,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) { tables->reinit_before_use(thd); } - lex->current_select= &lex->select_lex; + lex->current_select= lex->first_select_lex(); if (lex->result) @@ -4571,8 +4573,8 @@ bool Prepared_statement::validate_metadata(Prepared_statement *copy) if (is_sql_prepare() || lex->describe) return FALSE; - if (lex->select_lex.item_list.elements != - copy->lex->select_lex.item_list.elements) + if (lex->first_select_lex()->item_list.elements != + copy->lex->first_select_lex()->item_list.elements) { /** Column counts mismatch, update the client */ thd->server_status|= SERVER_STATUS_METADATA_CHANGED; @@ -4729,7 +4731,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) alloc_query(thd, (char*) expanded_query->ptr(), expanded_query->length())) { - my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), expanded_query->length()); + my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), expanded_query->length()); goto error; } /* @@ -5309,16 +5311,8 @@ bool Protocol_local::store_longlong(longlong value, bool unsigned_flag) bool Protocol_local::store_decimal(const my_decimal *value) { - char buf[DECIMAL_MAX_STR_LENGTH]; - String str(buf, sizeof (buf), &my_charset_bin); - int rc; - - rc= my_decimal2string(E_DEC_FATAL_ERROR, value, 0, 0, 0, &str); - - if (rc) - return TRUE; - - return store_column(str.ptr(), str.length()); + StringBuffer<DECIMAL_MAX_STR_LENGTH> str; + return value->to_string(&str) ? store_column(str.ptr(), str.length()) : true; } diff --git a/sql/sql_priv.h b/sql/sql_priv.h index e48b6195bb7..fa12b645041 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -183,7 +183,11 @@ #define OPTION_ALLOW_BATCH (1ULL << 36) // THD, intern (slave) #define OPTION_SKIP_REPLICATION (1ULL << 37) // THD, user #define OPTION_RPL_SKIP_PARALLEL (1ULL << 38) -#define OPTION_FOUND_COMMENT (1ULL << 39) // SELECT, intern, parser +#define OPTION_NO_QUERY_CACHE (1ULL << 39) // SELECT, user +#define OPTION_PROCEDURE_CLAUSE (1ULL << 40) // Internal usage + + +#define OPTION_LEX_FOUND_COMMENT (1ULL << 0) // intern, parser /* The rest of the file is included in the server only */ #ifndef MYSQL_CLIENT @@ -228,6 +232,7 @@ #define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29) #define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30) #define OPTIMIZER_SWITCH_SPLIT_MATERIALIZED (1ULL << 31) +#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY (1ULL << 32) #define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \ OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \ @@ -254,7 +259,8 @@ OPTIMIZER_SWITCH_EXISTS_TO_IN | \ OPTIMIZER_SWITCH_ORDERBY_EQ_PROP | \ OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED | \ - OPTIMIZER_SWITCH_SPLIT_MATERIALIZED) + OPTIMIZER_SWITCH_SPLIT_MATERIALIZED | \ + OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY) /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you @@ -356,6 +362,9 @@ enum enum_parsing_place IN_ORDER_BY, IN_UPDATE_ON_DUP_KEY, IN_PART_FUNC, + BEFORE_OPT_LIST, + AFTER_LIST, + FOR_LOOP_BOUND, PARSING_PLACE_SIZE /* always should be the last */ }; diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index 13f03fed5f3..6ca21aebb37 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -110,7 +110,7 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table) }; ST_FIELD_INFO *field_info; - Name_resolution_context *context= &thd->lex->select_lex.context; + Name_resolution_context *context= &thd->lex->first_select_lex()->context; int i; for (i= 0; schema_table->fields_info[i].field_name != NULL; i++) @@ -402,7 +402,7 @@ bool PROFILING::show_profiles() QUERY_PROFILE *prof; List<Item> field_list; MEM_ROOT *mem_root= thd->mem_root; - SELECT_LEX *sel= &thd->lex->select_lex; + SELECT_LEX *sel= thd->lex->first_select_lex(); SELECT_LEX_UNIT *unit= &thd->lex->unit; ha_rows idx= 0; Protocol *protocol= thd->protocol; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 4c0035b9c48..2ee175293de 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -3841,8 +3841,7 @@ int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len, if (!mysql_bin_log.is_open()) { my_message(ER_FLUSH_MASTER_BINLOG_CLOSED, - ER_THD(thd, ER_FLUSH_MASTER_BINLOG_CLOSED), - MYF(ME_BELL+ME_WAITTANG)); + ER_THD(thd, ER_FLUSH_MASTER_BINLOG_CLOSED), MYF(0)); return 1; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f1230666026..1780df61e56 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -354,7 +354,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, ulong setup_tables_done_option) { bool res; - SELECT_LEX *select_lex = &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); DBUG_ENTER("handle_select"); MYSQL_SELECT_START(thd->query()); @@ -1051,7 +1051,7 @@ JOIN::prepare(TABLE_LIST *tables_init, while ((select_el= select_it++)) { - if (select_el->with_sum_func) + if (select_el->with_sum_func()) found_sum_func_elem= true; if (select_el->with_field) found_field_elem= true; @@ -1219,14 +1219,14 @@ JOIN::prepare(TABLE_LIST *tables_init, item->max_length))) real_order= TRUE; - if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) + if (item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM) item->split_sum_func(thd, ref_ptrs, all_fields, 0); } if (!real_order) order= NULL; } - if (having && having->with_sum_func) + if (having && having->with_sum_func()) having->split_sum_func2(thd, ref_ptrs, all_fields, &having, SPLIT_SUM_SKIP_REGISTERED); if (select_lex->inner_sum_func_list) @@ -1396,6 +1396,7 @@ err: bool JOIN::build_explain() { + DBUG_ENTER("JOIN::build_explain"); create_explain_query_if_not_exists(thd->lex, thd->mem_root); have_query_plan= QEP_AVAILABLE; @@ -1413,8 +1414,7 @@ bool JOIN::build_explain() thd->mem_root= old_mem_root; DBUG_ASSERT(thd->free_list == old_free_list); // no Items were created if (res) - return 1; - + DBUG_RETURN(1); uint select_nr= select_lex->select_number; JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt(); for (uint i= 0; i < aggr_tables; i++, curr_tab++) @@ -1432,7 +1432,7 @@ bool JOIN::build_explain() get_using_temporary_read_tracker(); } } - return 0; + DBUG_RETURN(0); } @@ -1597,7 +1597,7 @@ JOIN::optimize_inner() { /* Item_cond_and can't be fixed after creation, so we do not check - conds->fixed + conds->is_fixed() */ conds->fix_fields(thd, &conds); conds->change_ref_to_fields(thd, tables_list); @@ -1641,7 +1641,7 @@ JOIN::optimize_inner() if (arena) thd->restore_active_arena(arena, &backup); } - + if (optimize_constant_subqueries()) DBUG_RETURN(1); @@ -1652,9 +1652,28 @@ JOIN::optimize_inner() (void) having->walk(&Item::cleanup_is_expensive_cache_processor, 0, (void *) 0); - if (setup_jtbm_semi_joins(this, join_list, &conds)) + List<Item> eq_list; + + if (setup_degenerate_jtbm_semi_joins(this, join_list, eq_list)) DBUG_RETURN(1); + if (eq_list.elements != 0) + { + Item *new_cond; + + if (eq_list.elements == 1) + new_cond= eq_list.pop(); + else + new_cond= new (thd->mem_root) Item_cond_and(thd, eq_list); + + if (new_cond && + ((new_cond->fix_fields(thd, &new_cond) || + !(conds= and_items(thd, conds, new_cond)) || + conds->fix_fields(thd, &conds)))) + DBUG_RETURN(TRUE); + } + eq_list.empty(); + if (select_lex->cond_pushed_into_where) { conds= and_conds(thd, conds, select_lex->cond_pushed_into_where); @@ -1685,6 +1704,31 @@ JOIN::optimize_inner() DBUG_RETURN(1); } + if (optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY)) + { + TABLE_LIST *tbl; + List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables); + while ((tbl= li++)) + if (tbl->jtbm_subselect) + { + if (tbl->jtbm_subselect->pushdown_cond_for_in_subquery(thd, conds)) + DBUG_RETURN(1); + } + } + + if (setup_jtbm_semi_joins(this, join_list, eq_list)) + DBUG_RETURN(1); + + if (eq_list.elements != 0) + { + conds= and_new_conditions_to_optimized_cond(thd, conds, &cond_equal, + eq_list, &cond_value); + + if (!conds && + cond_value != Item::COND_FALSE && cond_value != Item::COND_TRUE) + DBUG_RETURN(TRUE); + } + if (optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED)) { TABLE_LIST *tbl; @@ -1991,7 +2035,7 @@ int JOIN::optimize_stage2() if (!conds && outer_join) { /* Handle the case where we have an OUTER JOIN without a WHERE */ - conds= new (thd->mem_root) Item_int(thd, (longlong) 1,1); // Always true + conds= new (thd->mem_root) Item_bool(thd, true); // Always true } if (impossible_where) @@ -2130,7 +2174,7 @@ int JOIN::optimize_stage2() if (conds && const_table_map != found_const_table_map && (select_options & SELECT_DESCRIBE)) { - conds=new (thd->mem_root) Item_int(thd, (longlong) 0, 1); // Always false + conds=new (thd->mem_root) Item_bool(thd, false); // Always false } /* Cache constant expressions in WHERE, HAVING, ON clauses. */ @@ -2413,13 +2457,13 @@ int JOIN::optimize_stage2() elements may be lost during further having condition transformation in JOIN::exec. */ - if (having && const_table_map && !having->with_sum_func) + if (having && const_table_map && !having->with_sum_func()) { having->update_used_tables(); having= having->remove_eq_conds(thd, &select_lex->having_value, true); if (select_lex->having_value == Item::COND_FALSE) { - having= new (thd->mem_root) Item_int(thd, (longlong) 0,1); + having= new (thd->mem_root) Item_bool(thd, false); zero_result_cause= "Impossible HAVING noticed after reading const tables"; error= 0; select_lex->mark_const_derived(zero_result_cause); @@ -3753,6 +3797,15 @@ bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite, bool need_tmp_table, bool need_order, bool distinct) { + DBUG_ENTER("JOIN::save_explain_data"); + DBUG_PRINT("enter", ("Save explain Select_lex: %u (%p) parent lex: %p stmt_lex: %p present select: %u (%p)", + select_lex->select_number, select_lex, + select_lex->parent_lex, thd->lex->stmt_lex, + (output->get_select(select_lex->select_number) ? + select_lex->select_number : 0), + (output->get_select(select_lex->select_number) ? + output->get_select(select_lex->select_number) + ->select_lex : NULL))); /* If there is SELECT in this statement with the same number it must be the same SELECT @@ -3779,8 +3832,9 @@ bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite, /* It's a degenerate join */ message= zero_result_cause ? zero_result_cause : "No tables used"; } - return save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order, - distinct, message); + bool rc= save_explain_data_intern(thd->lex->explain, need_tmp_table, + need_order, distinct, message); + DBUG_RETURN(rc); } /* @@ -3802,11 +3856,11 @@ bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite, { if (!(join_tab[i].filesort->tracker= new Filesort_tracker(thd->lex->analyze_stmt))) - return 1; + DBUG_RETURN(1); } } } - return 0; + DBUG_RETURN(0); } @@ -4163,10 +4217,10 @@ mysql_select(THD *thd, is it single SELECT in derived table, called in derived table creation */ - if (select_lex->linkage != DERIVED_TABLE_TYPE || + if (select_lex->get_linkage() != DERIVED_TABLE_TYPE || (select_options & SELECT_DESCRIBE)) { - if (select_lex->linkage != GLOBAL_OPTIONS_TYPE) + if (select_lex->get_linkage() != GLOBAL_OPTIONS_TYPE) { /* Original join tabs might be overwritten at first @@ -4858,7 +4912,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (join->cond_value == Item::COND_FALSE) { join->impossible_where= true; - conds= new (join->thd->mem_root) Item_int(join->thd, (longlong) 0, 1); + conds= new (join->thd->mem_root) Item_bool(join->thd, false); } join->cond_equal= NULL; @@ -10620,7 +10674,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) below to check if we should use 'quick' instead. */ DBUG_PRINT("info", ("Item_int")); - tmp= new (thd->mem_root) Item_int(thd, (longlong) 1, 1); // Always true + tmp= new (thd->mem_root) Item_bool(thd, true); // Always true } } @@ -10748,7 +10802,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) Yet attributes of the just built condition are not needed. Thus we call sel->cond->quick_fix_field for safety. */ - if (sel->cond && !sel->cond->fixed) + if (sel->cond && !sel->cond->is_fixed()) sel->cond->quick_fix_field(); if (sel->test_quick_select(thd, tab->keys, @@ -12757,7 +12811,8 @@ void JOIN::join_free() !(select_options & SELECT_NO_UNLOCK) && !select_lex->subquery_in_having && (select_lex == (thd->lex->unit.fake_select_lex ? - thd->lex->unit.fake_select_lex : &thd->lex->select_lex))) + thd->lex->unit.fake_select_lex : + thd->lex->first_select_lex()))) { /* TODO: unlock tables even if the join isn't top level select in the @@ -13049,7 +13104,7 @@ static void update_depend_map_for_order(JOIN *join, ORDER *order) order->used= 0; // Not item_sum(), RAND() and no reference to table outside of sub select if (!(order->depend_map & (OUTER_REF_TABLE_BIT | RAND_TABLE_BIT)) - && !order->item[0]->with_sum_func && + && !order->item[0]->with_sum_func() && join->join_tab) { for (JOIN_TAB **tab=join->map2table; @@ -13140,7 +13195,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, for (order=first_order; order ; order=order->next) { table_map order_tables=order->item[0]->used_tables(); - if (order->item[0]->with_sum_func || + if (order->item[0]->with_sum_func() || /* If the outer table of an outer join is const (either by itself or after applying WHERE condition), grouping on a field from such a @@ -13316,7 +13371,7 @@ ORDER *simple_remove_const(ORDER *order, COND *where) ORDER *first= NULL, *prev= NULL; for (; order; order= order->next) { - DBUG_ASSERT(!order->item[0]->with_sum_func); // should never happen + DBUG_ASSERT(!order->item[0]->with_sum_func()); // should never happen if (!const_expression_in_where(where, order->item[0])) { if (!first) @@ -13565,9 +13620,9 @@ finish: FALSE otherwise */ -static bool check_simple_equality(THD *thd, const Item::Context &ctx, - Item *left_item, Item *right_item, - COND_EQUAL *cond_equal) +bool check_simple_equality(THD *thd, const Item::Context &ctx, + Item *left_item, Item *right_item, + COND_EQUAL *cond_equal) { Item *orig_left_item= left_item; Item *orig_right_item= right_item; @@ -14020,7 +14075,7 @@ COND *Item_cond_and::build_equal_items(THD *thd, if (!cond_args->elements && !cond_equal.current_level.elements && !eq_list.elements) - return new (thd->mem_root) Item_int(thd, (longlong) 1, 1); + return new (thd->mem_root) Item_bool(thd, true); List_iterator_fast<Item_equal> it(cond_equal.current_level); while ((item_equal= it++)) @@ -14127,7 +14182,7 @@ COND *Item_func_eq::build_equal_items(THD *thd, Item_equal *item_equal; int n= cond_equal.current_level.elements + eq_list.elements; if (n == 0) - return new (thd->mem_root) Item_int(thd, (longlong) 1, 1); + return new (thd->mem_root) Item_bool(thd, true); else if (n == 1) { if ((item_equal= cond_equal.current_level.pop())) @@ -14518,7 +14573,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels, List<Item> eq_list; Item_func_eq *eq_item= 0; if (((Item *) item_equal)->const_item() && !item_equal->val_int()) - return new (thd->mem_root) Item_int(thd, (longlong) 0, 1); + return new (thd->mem_root) Item_bool(thd, false); Item *item_const= item_equal->get_const(); Item_equal_fields_iterator it(*item_equal); Item *head; @@ -14526,7 +14581,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels, Item *current_sjm_head= NULL; DBUG_ASSERT(!cond || - cond->type() == Item::INT_ITEM || + cond->is_bool_literal() || (cond->type() == Item::FUNC_ITEM && ((Item_func *) cond)->functype() == Item_func::EQ_FUNC) || (cond->type() == Item::COND_ITEM && @@ -14647,13 +14702,13 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels, cond AND eq_1 AND eq_2 AND eq_3 AND ... - 'cond' is a parameter for this function, which may be NULL, an Item_int(1), + 'cond' is a parameter for this function, which may be NULL, an Item_bool(1), or an Item_func_eq or an Item_cond_and. We want to return a well-formed condition: no nested Item_cond_and objects, or Item_cond_and with a single child: - if 'cond' is an Item_cond_and, we add eq_i as its tail - - if 'cond' is Item_int(1), we return eq_i + - if 'cond' is Item_bool(1), we return eq_i - otherwise, we create our own Item_cond_and and put 'cond' at the front of it. - if we have only one condition to return, we don't create an Item_cond_and @@ -14665,10 +14720,10 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels, switch (eq_list.elements) { case 0: - res= cond ? cond : new (thd->mem_root) Item_int(thd, (longlong) 1, 1); + res= cond ? cond : new (thd->mem_root) Item_bool(thd, true); break; case 1: - if (!cond || cond->type() == Item::INT_ITEM) + if (!cond || cond->is_bool_literal()) res= eq_item; break; default: @@ -14810,7 +14865,7 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab, eq_cond= 0; break; } - else if (eq_cond->type() == Item::INT_ITEM && !eq_cond->val_bool()) + else if (eq_cond->is_bool_literal() && !eq_cond->val_bool()) { /* This occurs when eliminate_item_equal() founds that cond is @@ -14835,7 +14890,7 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab, else { /* Do not add an equality condition if it's always true */ - if (eq_cond->type() != Item::INT_ITEM && + if (!eq_cond->is_bool_literal() && cond_list->push_front(eq_cond, thd->mem_root)) eq_cond= 0; } @@ -15349,7 +15404,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top, conds= and_conds(join->thd, conds, table->on_expr); conds->top_level_item(); /* conds is always a new item as both cond and on_expr existed */ - DBUG_ASSERT(!conds->fixed); + DBUG_ASSERT(!conds->is_fixed()); conds->fix_fields(join->thd, &conds); } else @@ -16417,9 +16472,8 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value, { Field *field= ((Item_field*) real_item)->field; - if (((field->type() == MYSQL_TYPE_DATE) || - (field->type() == MYSQL_TYPE_DATETIME)) && - (field->flags & NOT_NULL_FLAG)) + if ((field->flags & NOT_NULL_FLAG) && + field->type_handler()->cond_notnull_field_isnull_to_field_eq_zero()) { /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */ /* @@ -16435,7 +16489,7 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value, */ - Item *item0= new(thd->mem_root) Item_int(thd, (longlong) 0, 1); + Item *item0= new(thd->mem_root) Item_bool(thd, false); Item *eq_cond= new(thd->mem_root) Item_func_eq(thd, args[0], item0); if (!eq_cond) return this; @@ -16505,7 +16559,7 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value, cond= new_cond; /* Item_func_eq can't be fixed after creation so we do not check - cond->fixed, also it do not need tables so we use 0 as second + cond->is_fixed(), also it do not need tables so we use 0 as second argument. */ cond->fix_fields(thd, &cond); @@ -16665,60 +16719,6 @@ const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field, Create internal temporary table ****************************************************************************/ -/** - Create field for temporary table from given field. - - @param thd Thread handler - @param org_field field from which new field will be created - @param name New field name - @param table Temporary table - @param item !=NULL if item->result_field should point to new field. - This is relevant for how fill_record() is going to work: - If item != NULL then fill_record() will update - the record in the original table. - If item == NULL then fill_record() will update - the temporary table - - @retval - NULL on error - @retval - new_created field -*/ - -Field *create_tmp_field_from_field(THD *thd, Field *org_field, - LEX_CSTRING *name, TABLE *table, - Item_field *item) -{ - Field *new_field; - - new_field= org_field->make_new_field(thd->mem_root, table, - table == org_field->table); - if (new_field) - { - new_field->init(table); - new_field->orig_table= org_field->orig_table; - if (item) - item->result_field= new_field; - else - new_field->field_name= *name; - new_field->flags|= org_field->flags & NO_DEFAULT_VALUE_FLAG; - if (org_field->maybe_null() || (item && item->maybe_null)) - new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join - if (org_field->type() == MYSQL_TYPE_VAR_STRING || - org_field->type() == MYSQL_TYPE_VARCHAR) - table->s->db_create_options|= HA_OPTION_PACK_RECORD; - else if (org_field->type() == FIELD_TYPE_DOUBLE) - ((Field_double *) new_field)->not_fixed= TRUE; - new_field->vcol_info= 0; - new_field->cond_selectivity= 1.0; - new_field->next_equal_field= NULL; - new_field->option_list= NULL; - new_field->option_struct= NULL; - } - return new_field; -} - - Field *Item::create_tmp_field_int(TABLE *table, uint convert_int_length) { const Type_handler *h= &type_handler_long; @@ -16728,6 +16728,22 @@ Field *Item::create_tmp_field_int(TABLE *table, uint convert_int_length) *this, table); } +Field *Item::tmp_table_field_from_field_type_maybe_null(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param, + bool is_explicit_null) +{ + DBUG_ASSERT(!param->make_copy_field()); + DBUG_ASSERT(!is_result_field()); + Field *result; + if ((result= tmp_table_field_from_field_type(table))) + { + if (result && is_explicit_null) + result->is_created_from_null_item= true; + } + return result; +} + Field *Item_sum::create_tmp_field(bool group, TABLE *table) { @@ -16759,57 +16775,6 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table) } -static void create_tmp_field_from_item_finalize(THD *thd, - Field *new_field, - Item *item, - Item ***copy_func, - bool modify_item) -{ - if (copy_func && - (item->is_result_field() || - (item->real_item()->is_result_field()))) - *((*copy_func)++) = item; // Save for copy_funcs - if (modify_item) - item->set_result_field(new_field); - if (item->type() == Item::NULL_ITEM) - new_field->is_created_from_null_item= TRUE; -} - - -/** - Create field for temporary table using type of given item. - - @param thd Thread handler - @param item Item to create a field for - @param table Temporary table - @param copy_func If set and item is a function, store copy of - item in this array - @param modify_item 1 if item->result_field should point to new - item. This is relevent for how fill_record() - is going to work: - If modify_item is 1 then fill_record() will - update the record in the original table. - If modify_item is 0 then fill_record() will - update the temporary table - - @retval - 0 on error - @retval - new_created field -*/ - -static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, - Item ***copy_func, bool modify_item) -{ - Field *UNINIT_VAR(new_field); - DBUG_ASSERT(thd == table->in_use); - if ((new_field= item->create_tmp_field(false, table))) - create_tmp_field_from_item_finalize(thd, new_field, item, - copy_func, modify_item); - return new_field; -} - - /** Create field for information schema table. @@ -16847,19 +16812,182 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table) /** + Create a temporary field for Item_field (or its descendant), + either direct or referenced by an Item_ref. +*/ +Field * +Item_field::create_tmp_field_from_item_field(TABLE *new_table, + Item_ref *orig_item, + const Tmp_field_param *param) +{ + DBUG_ASSERT(!is_result_field()); + Field *result; + /* + If item have to be able to store NULLs but underlaid field can't do it, + create_tmp_field_from_field() can't be used for tmp field creation. + */ + if (((maybe_null && in_rollup) || + (new_table->in_use->create_tmp_table_for_derived && /* for mat. view/dt */ + orig_item && orig_item->maybe_null)) && + !field->maybe_null()) + { + /* + The item the ref points to may have maybe_null flag set while + the ref doesn't have it. This may happen for outer fields + when the outer query decided at some point after name resolution phase + that this field might be null. Take this into account here. + */ + Record_addr rec(orig_item ? orig_item->maybe_null : maybe_null); + const Type_handler *handler= type_handler()-> + type_handler_for_tmp_table(this); + result= handler->make_and_init_table_field(orig_item ? &orig_item->name : &name, + rec, *this, new_table); + } + else if (param->table_cant_handle_bit_fields() && + field->type() == MYSQL_TYPE_BIT) + { + const Type_handler *handler= type_handler_long_or_longlong(); + result= handler->make_and_init_table_field(&name, + Record_addr(maybe_null), + *this, new_table); + } + else + { + LEX_CSTRING *tmp= orig_item ? &orig_item->name : &name; + bool tmp_maybe_null= param->modify_item() ? maybe_null : + field->maybe_null(); + result= field->create_tmp_field(new_table->in_use->mem_root, new_table, + tmp_maybe_null); + if (result) + result->field_name= *tmp; + } + if (result && param->modify_item()) + result_field= result; + return result; +} + + +Field *Item_field::create_tmp_field_ex(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param) +{ + DBUG_ASSERT(!is_result_field()); + Field *result; + src->set_field(field); + if (!(result= create_tmp_field_from_item_field(table, NULL, param))) + return NULL; + /* + Fields that are used as arguments to the DEFAULT() function already have + their data pointers set to the default value during name resolution. See + Item_default_value::fix_fields. + */ + if (type() != Item::DEFAULT_VALUE_ITEM && field->eq_def(result)) + src->set_default_field(field); + return result; +} + + +Field *Item_ref::create_tmp_field_ex(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param) +{ + Item *item= real_item(); + DBUG_ASSERT(is_result_field()); + if (item->type() == Item::FIELD_ITEM) + { + Field *result; + Item_field *field= (Item_field*) item; + Tmp_field_param prm2(*param); + prm2.set_modify_item(false); + src->set_field(field->field); + if (!(result= field->create_tmp_field_from_item_field(table, this, &prm2))) + return NULL; + if (param->modify_item()) + result_field= result; + return result; + } + return Item_result_field::create_tmp_field_ex(table, src, param); +} + + +void Item_result_field::get_tmp_field_src(Tmp_field_src *src, + const Tmp_field_param *param) +{ + if (param->make_copy_field()) + { + DBUG_ASSERT(result_field); + src->set_field(result_field); + } + else + { + src->set_item_result_field(this); // Save for copy_funcs + } +} + + +Field *Item_result_field::create_tmp_field_ex(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param) +{ + /* + Possible Item types: + - Item_cache_wrapper (only for CREATE..SELECT ?) + - Item_func + - Item_subselect + */ + DBUG_ASSERT(is_result_field()); + DBUG_ASSERT(type() != NULL_ITEM); + get_tmp_field_src(src, param); + Field *result; + if ((result= tmp_table_field_from_field_type(table)) && param->modify_item()) + result_field= result; + return result; +} + + +Field *Item_func_user_var::create_tmp_field_ex(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param) +{ + DBUG_ASSERT(is_result_field()); + DBUG_ASSERT(type() != NULL_ITEM); + get_tmp_field_src(src, param); + Field *result; + if ((result= create_table_field_from_handler(table)) && param->modify_item()) + result_field= result; + return result; +} + + +Field *Item_func_sp::create_tmp_field_ex(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param) +{ + Field *result; + get_tmp_field_src(src, param); + if ((result= sp_result_field->create_tmp_field(table->in_use->mem_root, + table))) + { + result->field_name= name; + if (param->modify_item()) + result_field= result; + } + return result; +} + +/** Create field for temporary table. - @param thd Thread handler - @param table Temporary table - @param item Item to create a field for - @param type Type of item (normally item->type) - @param copy_func If set and item is a function, store copy of item + @param table Temporary table + @param item Item to create a field for + @param type Type of item (normally item->type) + @param copy_func If set and item is a function, store copy of item in this array @param from_field if field will be created using other field as example, pointer example field will be written here - @param default_field If field has a default value field, store it here - @param group 1 if we are going to do a relative group by on result - @param modify_item 1 if item->result_field should point to new item. + @param default_field If field has a default value field, store it here + @param group 1 if we are going to do a relative group by on result + @param modify_item 1 if item->result_field should point to new item. This is relevent for how fill_record() is going to work: If modify_item is 1 then fill_record() will update @@ -16868,175 +16996,28 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table) the temporary table @retval - 0 on error + 0 on error @retval new_created field + Create a temporary field for Item_field (or its descendant), + either direct or referenced by an Item_ref. */ - -Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, +Field *create_tmp_field(TABLE *table, Item *item, Item ***copy_func, Field **from_field, Field **default_field, bool group, bool modify_item, bool table_cant_handle_bit_fields, bool make_copy_field) { - Field *result; - Item::Type orig_type= type; - Item *orig_item= 0; - - DBUG_ASSERT(thd == table->in_use); - - if (type != Item::FIELD_ITEM && - item->real_item()->type() == Item::FIELD_ITEM) - { - orig_item= item; - item= item->real_item(); - type= Item::FIELD_ITEM; - } - - switch (type) { - case Item::TYPE_HOLDER: - case Item::SUM_FUNC_ITEM: - { - result= item->create_tmp_field(group, table); - if (!result) - my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); - return result; - } - case Item::FIELD_ITEM: - case Item::DEFAULT_VALUE_ITEM: - case Item::INSERT_VALUE_ITEM: - case Item::TRIGGER_FIELD_ITEM: - { - Item_field *field= (Item_field*) item; - bool orig_modify= modify_item; - if (orig_type == Item::REF_ITEM) - modify_item= 0; - /* - If item have to be able to store NULLs but underlaid field can't do it, - create_tmp_field_from_field() can't be used for tmp field creation. - */ - if (((field->maybe_null && field->in_rollup) || - (thd->create_tmp_table_for_derived && /* for mat. view/dt */ - orig_item && orig_item->maybe_null)) && - !field->field->maybe_null()) - { - bool save_maybe_null= FALSE; - /* - The item the ref points to may have maybe_null flag set while - the ref doesn't have it. This may happen for outer fields - when the outer query decided at some point after name resolution phase - that this field might be null. Take this into account here. - */ - if (orig_item) - { - save_maybe_null= item->maybe_null; - item->maybe_null= orig_item->maybe_null; - } - result= create_tmp_field_from_item(thd, item, table, NULL, - modify_item); - *from_field= field->field; - if (result && modify_item) - field->result_field= result; - if (orig_item) - { - item->maybe_null= save_maybe_null; - result->field_name= orig_item->name; - } - } - else if (table_cant_handle_bit_fields && field->field->type() == - MYSQL_TYPE_BIT) - { - const Type_handler *handler= item->type_handler_long_or_longlong(); - *from_field= field->field; - if ((result= - handler->make_and_init_table_field(&item->name, - Record_addr(item->maybe_null), - *item, table))) - create_tmp_field_from_item_finalize(thd, result, item, - copy_func, modify_item); - if (result && modify_item) - field->result_field= result; - } - else - { - LEX_CSTRING *tmp= orig_item ? &orig_item->name : &item->name; - result= create_tmp_field_from_field(thd, (*from_field= field->field), - tmp, table, - modify_item ? field : - NULL); - } - - if (orig_type == Item::REF_ITEM && orig_modify) - ((Item_ref*)orig_item)->set_result_field(result); - /* - Fields that are used as arguments to the DEFAULT() function already have - their data pointers set to the default value during name resolution. See - Item_default_value::fix_fields. - */ - if (orig_type != Item::DEFAULT_VALUE_ITEM && field->field->eq_def(result)) - *default_field= field->field; - return result; - } - /* Fall through */ - case Item::FUNC_ITEM: - if (((Item_func *) item)->functype() == Item_func::FUNC_SP) - { - Item_func_sp *item_func_sp= (Item_func_sp *) item; - Field *sp_result_field= item_func_sp->get_sp_result_field(); - - if (make_copy_field) - { - DBUG_ASSERT(item_func_sp->result_field); - *from_field= item_func_sp->result_field; - } - else - { - *((*copy_func)++)= item; - } - Field *result_field= - create_tmp_field_from_field(thd, - sp_result_field, - &item_func_sp->name, - table, - NULL); - - if (modify_item) - item->set_result_field(result_field); - - return result_field; - } - - /* Fall through */ - case Item::COND_ITEM: - case Item::FIELD_AVG_ITEM: - case Item::FIELD_STD_ITEM: - case Item::SUBSELECT_ITEM: - /* The following can only happen with 'CREATE TABLE ... SELECT' */ - case Item::PROC_ITEM: - case Item::INT_ITEM: - case Item::REAL_ITEM: - case Item::DECIMAL_ITEM: - case Item::STRING_ITEM: - case Item::DATE_ITEM: - case Item::REF_ITEM: - case Item::NULL_ITEM: - case Item::VARBIN_ITEM: - case Item::CACHE_ITEM: - case Item::WINDOW_FUNC_ITEM: // psergey-winfunc: - case Item::EXPR_CACHE_ITEM: - case Item::PARAM_ITEM: - if (make_copy_field) - { - DBUG_ASSERT(((Item_result_field*)item)->result_field); - *from_field= ((Item_result_field*)item)->result_field; - } - return create_tmp_field_from_item(thd, item, table, - (make_copy_field ? 0 : copy_func), - modify_item); - default: // Dosen't have to be stored - return 0; - } + Tmp_field_src src; + Tmp_field_param prm(group, modify_item, table_cant_handle_bit_fields, + make_copy_field); + Field *result= item->create_tmp_field_ex(table, &src, &prm); + *from_field= src.field(); + *default_field= src.default_field(); + if (src.item_result_field()) + *((*copy_func)++)= src.item_result_field(); + return result; } /* @@ -17052,7 +17033,7 @@ setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps, uint field_count) { uint bitmap_size= bitmap_buffer_size(field_count); - DBUG_ASSERT(table->s->virtual_fields == 0 && table->def_vcol_set == 0); + DBUG_ASSERT(table->s->virtual_fields == 0); my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, FALSE); @@ -17317,7 +17298,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, } if (not_all_columns) { - if (item->with_sum_func && type != Item::SUM_FUNC_ITEM) + if (item->with_sum_func() && type != Item::SUM_FUNC_ITEM) { if (item->used_tables() & OUTER_REF_TABLE_BIT) item->update_used_tables(); @@ -17347,7 +17328,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, { Item *tmp_item; Field *new_field= - create_tmp_field(thd, table, arg, arg->type(), ©_func, + create_tmp_field(table, arg, ©_func, tmp_from_field, &default_field[fieldnr], group != 0,not_all_columns, distinct, false); @@ -17397,7 +17378,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, else { /* - The last parameter to create_tmp_field() is a bit tricky: + The last parameter to create_tmp_field_ex() is a bit tricky: We need to set it to 0 in union, to get fill_record() to modify the temporary table. @@ -17411,7 +17392,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, */ Field *new_field= (param->schema_table) ? item->create_field_for_schema(thd, table) : - create_tmp_field(thd, table, item, type, ©_func, + create_tmp_field(table, item, ©_func, tmp_from_field, &default_field[fieldnr], group != 0, !force_copy_fields && @@ -17425,8 +17406,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, */ item->marker == 4 || param->bit_fields_as_long, force_copy_fields); - - if (unlikely(!new_field)) + if (!new_field) { if (unlikely(thd->is_fatal_error)) goto err; // Got OOM @@ -18007,12 +17987,10 @@ bool Virtual_tmp_table::add(List<Spvar_definition> &field_list) while ((cdef= it++)) { Field *tmp; - if (!(tmp= cdef->make_field(s, in_use->mem_root, 0, - (uchar*) (f_maybe_null(cdef->pack_flag) ? "" : 0), - f_maybe_null(cdef->pack_flag) ? 1 : 0, - &cdef->field_name))) + Record_addr addr(f_maybe_null(cdef->pack_flag)); + if (!(tmp= cdef->make_field(s, in_use->mem_root, &addr, &cdef->field_name))) DBUG_RETURN(true); - add(tmp); + add(tmp); } DBUG_RETURN(false); } @@ -18132,7 +18110,7 @@ bool Virtual_tmp_table::sp_set_all_fields_from_item_list(THD *thd, bool Virtual_tmp_table::sp_set_all_fields_from_item(THD *thd, Item *value) { - DBUG_ASSERT(value->fixed); + DBUG_ASSERT(value->is_fixed()); DBUG_ASSERT(value->cols() == s->fields); for (uint i= 0; i < value->cols(); i++) { @@ -18555,7 +18533,7 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table, We don't want this error to be converted to a warning, e.g. in case of INSERT IGNORE ... SELECT. */ - table->file->print_error(error, MYF(ME_FATALERROR)); + table->file->print_error(error, MYF(ME_FATAL)); DBUG_RETURN(1); } new_table= *table; @@ -18578,7 +18556,7 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table, new_table.no_rows= table->no_rows; if (create_internal_tmp_table(&new_table, table->key_info, start_recinfo, recinfo, - thd->lex->select_lex.options | + thd->lex->first_select_lex()->options | thd->variables.option_bits)) goto err2; if (open_tmp_table(&new_table)) @@ -22729,7 +22707,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field, if (unlikely(copy_blobs(first_field))) { my_message(ER_OUTOFMEMORY, ER_THD(thd,ER_OUTOFMEMORY), - MYF(ME_FATALERROR)); + MYF(ME_FATAL)); error=0; goto err; } @@ -23004,12 +22982,7 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array, uint counter; enum_resolution_type resolution; - /* - Local SP variables may be int but are expressions, not positions. - (And they can't be used before fix_fields is called for them). - */ - if (order_item->type() == Item::INT_ITEM && order_item->basic_const_item() && - !from_window_spec) + if (order_item->is_order_clause_position() && !from_window_spec) { /* Order by position */ uint count; if (order->counter_used) @@ -23123,7 +23096,7 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array, inspite of that fix_fields() calls find_item_in_list() one more time. - We check order_item->fixed because Item_func_group_concat can put + We check order_item->is_fixed() because Item_func_group_concat can put arguments for which fix_fields already was called. */ if (order_item->fix_fields_if_needed_for_order_by(thd, order->item) || @@ -23233,7 +23206,7 @@ setup_group(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, all_fields, true, true, from_window_spec)) return 1; (*ord->item)->marker= UNDEF_POS; /* Mark found */ - if ((*ord->item)->with_sum_func && context_analysis_place == IN_GROUP_BY) + if ((*ord->item)->with_sum_func() && context_analysis_place == IN_GROUP_BY) { my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*ord->item)->full_name()); return 1; @@ -23392,7 +23365,7 @@ create_distinct_group(THD *thd, Ref_ptr_array ref_pointer_array, li.rewind(); while ((item=li++)) { - if (!item->const_item() && !item->with_sum_func && !item->marker) + if (!item->const_item() && !item->with_sum_func() && !item->marker) { /* Don't put duplicate columns from the SELECT list into the @@ -23489,9 +23462,11 @@ count_field_types(SELECT_LEX *select_lex, TMP_TABLE_PARAM *param, } else { + With_sum_func_cache *cache= field->get_with_sum_func_cache(); param->func_count++; - if (reset_with_sum_func) - field->with_sum_func=0; + // "field" can point to Item_std_field, so "cache" can be NULL here. + if (reset_with_sum_func && cache) + cache->reset_with_sum_func(); } } } @@ -23631,7 +23606,7 @@ void calc_group_buffer(TMP_TABLE_PARAM *param, ORDER *group) { /* Group strings are taken as varstrings and require an length field. - A field is not yet created by create_tmp_field() + A field is not yet created by create_tmp_field_ex() and the sizes should match up. */ key_length+= group_item->max_length + HA_KEY_BLOB_LENGTH; @@ -23641,7 +23616,7 @@ void calc_group_buffer(TMP_TABLE_PARAM *param, ORDER *group) default: /* This case should never be choosen */ DBUG_ASSERT(0); - my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); + my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL)); } } parts++; @@ -23895,7 +23870,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, real_pos->real_type() == Item::SUBSELECT_ITEM || real_pos->type() == Item::CACHE_ITEM || real_pos->type() == Item::COND_ITEM) && - !real_pos->with_sum_func) + !real_pos->with_sum_func()) { // Save for send fields pos= real_pos; /* TODO: @@ -24102,7 +24077,7 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, for (uint i= 0; (item= it++); i++) { Field *field; - if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) + if (item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM) item_field= item; else if (item->type() == Item::FIELD_ITEM) { @@ -24410,7 +24385,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) } if (unlikely(thd->is_fatal_error)) DBUG_RETURN(TRUE); - if (!cond->fixed) + if (!cond->is_fixed()) { Item *tmp_item= (Item*) cond; cond->fix_fields(thd, &tmp_item); @@ -24629,7 +24604,7 @@ bool JOIN::rollup_init() Marking the expression item as 'with_sum_func' will ensure this. */ if (changed) - item->with_sum_func= 1; + item->get_with_sum_func_cache()->set_with_sum_func(); } } return 0; @@ -25117,7 +25092,8 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta, */ if (real_table->merged_for_insert) { - TABLE_LIST *view_child= real_table->view->select_lex.table_list.first; + TABLE_LIST *view_child= + real_table->view->first_select_lex()->table_list.first; for (;view_child; view_child= view_child->next_local) { if (view_child->table == table) @@ -25570,8 +25546,9 @@ int JOIN::save_explain_data_intern(Explain_query *output, { JOIN *join= this; /* Legacy: this code used to be a non-member function */ DBUG_ENTER("JOIN::save_explain_data_intern"); - DBUG_PRINT("info", ("Select %p, type %s, message %s", - join->select_lex, join->select_lex->type, + DBUG_PRINT("info", ("Select %p (%u), type %s, message %s", + join->select_lex, join->select_lex->select_number, + join->select_lex->type, message ? message : "NULL")); DBUG_ASSERT(have_query_plan == QEP_AVAILABLE); /* fake_select_lex is created/printed by Explain_union */ @@ -25597,7 +25574,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, explain->select_id= join->select_lex->select_number; explain->select_type= join->select_lex->type; - explain->linkage= select_lex->linkage; + explain->linkage= select_lex->get_linkage(); explain->using_temporary= need_tmp; explain->using_filesort= need_order_arg; /* Setting explain->message means that all other members are invalid */ @@ -25620,7 +25597,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, explain->select_id= select_lex->select_number; explain->select_type= select_lex->type; - explain->linkage= select_lex->linkage; + explain->linkage= select_lex->get_linkage(); explain->using_temporary= need_tmp; explain->using_filesort= need_order_arg; explain->message= "Storage engine handles GROUP BY"; @@ -25643,7 +25620,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, join->select_lex->set_explain_type(true); xpl_sel->select_id= join->select_lex->select_number; xpl_sel->select_type= join->select_lex->type; - xpl_sel->linkage= select_lex->linkage; + xpl_sel->linkage= select_lex->get_linkage(); if (select_lex->master_unit()->derived) xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; @@ -25787,7 +25764,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, for such queries, we'll get here before having called subquery_expr->fix_fields(), which will cause failure to */ - if (unit->item && !unit->item->fixed) + if (unit->item && !unit->item->is_fixed()) { Item *ref= unit->item; if (unit->item->fix_fields(thd, &ref)) @@ -26226,6 +26203,18 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) { str->append("/* select#"); str->append_ulonglong(select_number); + if (thd->lex->describe & DESCRIBE_EXTENDED2) + { + str->append("/"); + str->append_ulonglong(nest_level); + + if (master_unit()->fake_select_lex && + master_unit()->first_select() == this) + { + str->append(" Filter Select: "); + master_unit()->fake_select_lex->print(thd, str, query_type); + } + } str->append(" */ "); } @@ -26257,18 +26246,21 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) str->append(STRING_WITH_LEN("sql_buffer_result ")); if (options & OPTION_FOUND_ROWS) str->append(STRING_WITH_LEN("sql_calc_found_rows ")); - switch (sql_cache) + if (this == parent_lex->first_select_lex()) { - case SQL_NO_CACHE: - str->append(STRING_WITH_LEN("sql_no_cache ")); - break; - case SQL_CACHE: - str->append(STRING_WITH_LEN("sql_cache ")); - break; - case SQL_CACHE_UNSPECIFIED: - break; - default: - DBUG_ASSERT(0); + switch (parent_lex->sql_cache) + { + case LEX::SQL_NO_CACHE: + str->append(STRING_WITH_LEN("sql_no_cache ")); + break; + case LEX::SQL_CACHE: + str->append(STRING_WITH_LEN("sql_cache ")); + break; + case LEX::SQL_CACHE_UNSPECIFIED: + break; + default: + DBUG_ASSERT(0); + } } //Item List diff --git a/sql/sql_select.h b/sql/sql_select.h index 0e486c1fbec..e2c78473b1a 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1477,6 +1477,11 @@ public: Dynamic_array<KEYUSE_EXT> *ext_keyuses_for_splitting; JOIN_TAB *sort_and_group_aggr_tab; + /* + Flag is set to true if select_lex was found to be degenerated before + the optimize_cond() call in JOIN::optimize_inner() method. + */ + bool is_orig_degenerated; JOIN(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg, select_result *result_arg) @@ -1572,6 +1577,7 @@ public: emb_sjm_nest= NULL; sjm_lookup_tables= 0; sjm_scan_tables= 0; + is_orig_degenerated= false; } /* True if the plan guarantees that it will be returned zero or one row */ @@ -1751,6 +1757,7 @@ public: bool fix_all_splittings_in_plan(); bool transform_in_predicates_into_in_subq(THD *thd); + bool add_equalities_to_where_condition(THD *thd, List<Item> &eq_list); private: /** Create a temporary table to be used for processing DISTINCT/ORDER @@ -1815,10 +1822,6 @@ bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, void copy_fields(TMP_TABLE_PARAM *param); bool copy_funcs(Item **func_ptr, const THD *thd); uint find_shortest_key(TABLE *table, const key_map *usable_keys); -Field* create_tmp_field_from_field(THD *thd, Field* org_field, - LEX_CSTRING *name, TABLE *table, - Item_field *item); - bool is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args); /* functions from opt_sum.cc */ @@ -2070,12 +2073,6 @@ bool mysql_select(THD *thd, void free_underlaid_joins(THD *thd, SELECT_LEX *select); bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result); -Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, - Item ***copy_func, Field **from_field, - Field **def_field, - bool group, bool modify_item, - bool table_cant_handle_bit_fields, - bool make_copy_field); /* General routine to change field->ptr of a NULL-terminated array of Field @@ -2343,7 +2340,7 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field, extern bool test_if_ref(Item *, Item_field *left_item,Item *right_item); -inline bool optimizer_flag(THD *thd, uint flag) +inline bool optimizer_flag(THD *thd, ulonglong flag) { return (thd->variables.optimizer_switch & flag); } @@ -2459,4 +2456,13 @@ int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort); JOIN_TAB *first_explain_order_tab(JOIN* join); JOIN_TAB *next_explain_order_tab(JOIN* join, JOIN_TAB* tab); +bool check_simple_equality(THD *thd, const Item::Context &ctx, + Item *left_item, Item *right_item, + COND_EQUAL *cond_equal); + +void propagate_new_equalities(THD *thd, Item *cond, + List<Item_equal> *new_equalities, + COND_EQUAL *inherited, + bool *is_simplifiable_cond); + #endif /* SQL_SELECT_INCLUDED */ diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc index 1fb2e5e7714..1ed0bb38e64 100644 --- a/sql/sql_sequence.cc +++ b/sql/sql_sequence.cc @@ -220,8 +220,8 @@ bool check_sequence_fields(LEX *lex, List<Create_field> *fields) err: my_error(ER_SEQUENCE_INVALID_TABLE_STRUCTURE, MYF(0), - lex->select_lex.table_list.first->db.str, - lex->select_lex.table_list.first->table_name.str, reason); + lex->first_select_lex()->table_list.first->db.str, + lex->first_select_lex()->table_list.first->table_name.str, reason); DBUG_RETURN(TRUE); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 426f40d1729..b98f8aabdc1 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1034,9 +1034,9 @@ find_files(THD *thd, Dynamic_array<LEX_CSTRING*> *files, LEX_CSTRING *db, if (!(dirp = my_dir(path, MY_THREAD_SPECIFIC | (db ? 0 : MY_WANT_STAT)))) { if (my_errno == ENOENT) - my_error(ER_BAD_DB_ERROR, MYF(ME_BELL | ME_WAITTANG), db->str); + my_error(ER_BAD_DB_ERROR, MYF(0), db->str); else - my_error(ER_CANT_READ_DIR, MYF(ME_BELL | ME_WAITTANG), path, my_errno); + my_error(ER_CANT_READ_DIR, MYF(0), path, my_errno); DBUG_RETURN(FIND_FILES_DIR); } @@ -2184,6 +2184,12 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, field->sql_type(type); packet->append(type.ptr(), type.length(), system_charset_info); + DBUG_EXECUTE_IF("sql_type", + packet->append(" /* "); + packet->append(field->type_handler()->version().ptr()); + packet->append(" */ "); + ); + if (field->has_charset() && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))) { if (field->charset() != share->table_charset) @@ -4143,8 +4149,9 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables, case SQLCOM_SHOW_TABLE_STATUS: case SQLCOM_SHOW_TRIGGERS: case SQLCOM_SHOW_EVENTS: - thd->make_lex_string(&lookup_field_values->db_value, - lex->select_lex.db.str, lex->select_lex.db.length); + thd->make_lex_string(&lookup_field_values->db_value, + lex->first_select_lex()->db.str, + lex->first_select_lex()->db.length); if (wild) { thd->make_lex_string(&lookup_field_values->table_value, @@ -4537,10 +4544,10 @@ fill_schema_table_by_open(THD *thd, MEM_ROOT *mem_root, temporary LEX. The latter is required to correctly open views and produce table describing their structure. */ - if (make_table_list(thd, &lex->select_lex, &db_name, &table_name)) + if (make_table_list(thd, lex->first_select_lex(), &db_name, &table_name)) goto end; - table_list= lex->select_lex.table_list.first; + table_list= lex->first_select_lex()->table_list.first; if (is_show_fields_or_keys) { @@ -6713,7 +6720,7 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, & 'field_translation_end' are uninitialized is this case. */ - List<Item> *fields= &tables->view->select_lex.item_list; + List<Item> *fields= &tables->view->first_select_lex()->item_list; List_iterator<Item> it(*fields); Item *item; Item_field *field; @@ -7366,7 +7373,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, break; default: DBUG_ASSERT(0); - my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); + my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL)); DBUG_RETURN(1); } table->field[7]->set_notnull(); @@ -7728,9 +7735,9 @@ int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond) TABLE *table= tables->table; CHARSET_INFO *cs= system_charset_info; OPEN_TABLE_LIST *open_list; - if (unlikely(!(open_list= list_open_tables(thd, thd->lex->select_lex.db.str, - wild))) && - unlikely(thd->is_fatal_error)) + if (!(open_list= list_open_tables(thd, thd->lex->first_select_lex()->db.str, + wild)) + && thd->is_fatal_error) DBUG_RETURN(1); for (; open_list ; open_list=open_list->next) @@ -8224,7 +8231,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) tmp_table_param->table_charset= cs; tmp_table_param->field_count= field_count; tmp_table_param->schema_table= 1; - SELECT_LEX *select_lex= thd->lex->current_select; + SELECT_LEX *select_lex= table_list->select_lex; bool keep_row_order= is_show_command(thd); if (!(table= create_tmp_table(thd, tmp_table_param, field_list, (ORDER*) 0, 0, 0, @@ -8261,7 +8268,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) static int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) { ST_FIELD_INFO *field_info= schema_table->fields_info; - Name_resolution_context *context= &thd->lex->select_lex.context; + Name_resolution_context *context= &thd->lex->first_select_lex()->context; for (; field_info->field_name; field_info++) { if (field_info->old_name) @@ -8321,14 +8328,14 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) char tmp[128]; String buffer(tmp,sizeof(tmp), thd->charset()); LEX *lex= thd->lex; - Name_resolution_context *context= &lex->select_lex.context; + Name_resolution_context *context= &lex->first_select_lex()->context; ST_FIELD_INFO *field_info= &schema_table->fields_info[2]; LEX_CSTRING field_name= {field_info->field_name, strlen(field_info->field_name) }; buffer.length(0); buffer.append(field_info->old_name); - buffer.append(&lex->select_lex.db); + buffer.append(&lex->first_select_lex()->db); if (lex->wild && lex->wild->ptr()) { buffer.append(STRING_WITH_LEN(" (")); @@ -8361,7 +8368,7 @@ int make_columns_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) int fields_arr[]= {3, 15, 14, 6, 16, 5, 17, 18, 19, -1}; int *field_num= fields_arr; ST_FIELD_INFO *field_info; - Name_resolution_context *context= &thd->lex->select_lex.context; + Name_resolution_context *context= &thd->lex->first_select_lex()->context; for (; *field_num >= 0; field_num++) { @@ -8392,7 +8399,7 @@ int make_character_sets_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) int fields_arr[]= {0, 2, 1, 3, -1}; int *field_num= fields_arr; ST_FIELD_INFO *field_info; - Name_resolution_context *context= &thd->lex->select_lex.context; + Name_resolution_context *context= &thd->lex->first_select_lex()->context; for (; *field_num >= 0; field_num++) { @@ -8419,7 +8426,7 @@ int make_proc_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) int fields_arr[]= {2, 3, 4, 27, 24, 23, 22, 26, 28, 29, 30, -1}; int *field_num= fields_arr; ST_FIELD_INFO *field_info; - Name_resolution_context *context= &thd->lex->select_lex.context; + Name_resolution_context *context= &thd->lex->first_select_lex()->context; for (; *field_num >= 0; field_num++) { diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc index a92d40f6bb3..83abaebd4fd 100644 --- a/sql/sql_signal.cc +++ b/sql/sql_signal.cc @@ -323,7 +323,7 @@ end: set= m_set_signal_information.m_item[i]; if (set) { - if (set->fixed) + if (set->is_fixed()) set->cleanup(); } } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index cc77452ecd1..39d9438d5bf 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -103,8 +103,7 @@ bool String::realloc_raw(size_t alloc_length) (thread_specific ? MY_THREAD_SPECIFIC : 0))))) { - if (str_length > len - 1) - str_length= 0; + DBUG_ASSERT(str_length < len); if (str_length) // Avoid bugs in memcpy on AIX memcpy(new_ptr,Ptr,str_length); new_ptr[str_length]=0; diff --git a/sql/sql_string.h b/sql/sql_string.h index 23783405b19..0ae68cb3796 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -762,6 +762,17 @@ public: }; +class String_space: public String +{ +public: + String_space(uint n) + { + if (fill(n, ' ')) + set("", 0, &my_charset_bin); + } +}; + + static inline bool check_if_only_end_space(CHARSET_INFO *cs, const char *str, const char *end) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b9fc431feb1..3e204c4945b 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4248,11 +4248,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } /* Give warnings for not supported table options */ -#if defined(WITH_ARIA_STORAGE_ENGINE) extern handlerton *maria_hton; - if (file->partition_ht() != maria_hton) -#endif - if (create_info->transactional) + if (file->partition_ht() != maria_hton && create_info->transactional && + !file->has_transaction_manager()) push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, ER_THD(thd, ER_ILLEGAL_HA_CREATE_OPTION), @@ -4877,7 +4875,7 @@ int create_table_impl(THD *thd, /* Restart statement transactions for the case of CREATE ... SELECT. */ - if (thd->lex->select_lex.item_list.elements && + if (thd->lex->first_select_lex()->item_list.elements && restart_trans_for_tables(thd, thd->lex->query_tables)) goto err; } @@ -9550,8 +9548,6 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, } DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock"); - /* We can abort alter table for any table type */ - thd->abort_on_warning= !ignore && thd->is_strict_mode(); /* Create .FRM for new version of table with a temporary name. @@ -9581,7 +9577,6 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, C_ALTER_TABLE_FRM_ONLY, NULL, &key_info, &key_count, &frm); reenable_binlog(thd); - thd->abort_on_warning= false; if (unlikely(error)) { my_free(const_cast<uchar*>(frm.str)); @@ -10102,19 +10097,17 @@ err_new_table_cleanup: if (unlikely(alter_ctx.error_if_not_empty && thd->get_stmt_da()->current_row_for_warning())) { - const char *f_val= 0; - enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE; + const char *f_val= "0000-00-00"; + const char *f_type= "date"; switch (alter_ctx.datetime_field->real_field_type()) { case MYSQL_TYPE_DATE: case MYSQL_TYPE_NEWDATE: - f_val= "0000-00-00"; - t_type= MYSQL_TIMESTAMP_DATE; break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_DATETIME2: f_val= "0000-00-00 00:00:00"; - t_type= MYSQL_TIMESTAMP_DATETIME; + f_type= "datetime"; break; default: /* Shouldn't get here. */ @@ -10122,9 +10115,10 @@ err_new_table_cleanup: } bool save_abort_on_warning= thd->abort_on_warning; thd->abort_on_warning= true; - make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, - f_val, strlength(f_val), t_type, - alter_ctx.datetime_field->field_name.str); + thd->push_warning_truncated_value_for_field(Sql_condition::WARN_LEVEL_WARN, + f_type, f_val, + alter_ctx.datetime_field-> + field_name.str); thd->abort_on_warning= save_abort_on_warning; } @@ -10253,10 +10247,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff); - /* Set read map for all fields in from table */ from->default_column_bitmaps(); - bitmap_set_all(from->read_set); - from->file->column_bitmaps_signal(); /* We can abort alter table for any table type */ thd->abort_on_warning= !ignore && thd->is_strict_mode(); @@ -10286,7 +10277,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (def->field == from->found_next_number_field) thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO; } - (copy_end++)->set(*ptr,def->field,0); + if (!(*ptr)->vcol_info) + { + bitmap_set_bit(from->read_set, def->field->field_index); + (copy_end++)->set(*ptr,def->field,0); + } } else { @@ -10329,8 +10324,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, Filesort_tracker dummy_tracker(false); Filesort fsort(order, HA_POS_ERROR, true, NULL); - if (thd->lex->select_lex.setup_ref_array(thd, order_num) || - setup_order(thd, thd->lex->select_lex.ref_pointer_array, + if (thd->lex->first_select_lex()->setup_ref_array(thd, order_num) || + setup_order(thd, thd->lex->first_select_lex()->ref_pointer_array, &tables, fields, all_fields, order)) goto err; @@ -10355,6 +10350,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, from_row_end= from->vers_end_field(); } + if (from_row_end) + bitmap_set_bit(from->read_set, from_row_end->field_index); + + from->file->column_bitmaps_signal(); + THD_STAGE_INFO(thd, stage_copy_to_tmp_table); /* Tell handler that we have values for all columns in the to table */ to->use_all_columns(); @@ -10704,7 +10704,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, ha_checksum crc= 0; uchar null_mask=256 - (1 << t->s->last_null_bit_pos); - t->use_all_columns(); + t->use_all_stored_columns(); if (t->file->ha_rnd_init(1)) protocol->store_null(); diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 35ef1e50c36..630d150be77 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -175,7 +175,7 @@ int calc_weekday(long daynr,bool sunday_first_day_of_week) next week is week 1. */ -uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year) +uint calc_week(const MYSQL_TIME *l_time, uint week_behaviour, uint *year) { uint days; ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day); @@ -289,14 +289,14 @@ ulong convert_month_to_period(ulong month) bool -check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, +check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, date_mode_t fuzzydate, timestamp_type ts_type) { int unused; - if (check_date(ltime, fuzzy_date, &unused)) + if (check_date(ltime, fuzzydate, &unused)) { ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, &str, ts_type, 0); return true; } @@ -305,7 +305,7 @@ check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, bool -adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec) +adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec) { MYSQL_TIME copy= *ltime; ErrConvTime str(©); @@ -313,8 +313,7 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec) if (check_time_range(ltime, dec, &warnings)) return true; if (warnings) - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - &str, MYSQL_TIMESTAMP_TIME, NullS); + thd->push_warning_truncated_wrong_value("time", str.ptr()); return false; } @@ -352,33 +351,57 @@ to_ascii(CHARSET_INFO *cs, } -/* Character set-aware version of str_to_time() */ -bool -str_to_time(CHARSET_INFO *cs, const char *str, size_t length, - MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status) +class TemporalAsciiBuffer: public LEX_CSTRING { char cnv[32]; - if ((cs->state & MY_CS_NONASCII) != 0) +public: + TemporalAsciiBuffer(const char *str, size_t length, CHARSET_INFO *cs) { - length= to_ascii(cs, str, length, cnv, sizeof(cnv)); - str= cnv; + if ((cs->state & MY_CS_NONASCII) != 0) + { + LEX_CSTRING::str= cnv; + LEX_CSTRING::length= to_ascii(cs, str, length, cnv, sizeof(cnv)); + } + else + { + LEX_CSTRING::str= str; + LEX_CSTRING::length= length; + } } - return str_to_time(str, length, l_time, fuzzydate, status); +}; + + +/* Character set-aware version of str_to_time() */ +bool Temporal::str_to_time(MYSQL_TIME_STATUS *status, + const char *str, size_t length, CHARSET_INFO *cs, + date_mode_t fuzzydate) +{ + TemporalAsciiBuffer tmp(str, length, cs); + return ::str_to_time(tmp.str, tmp.length, this, + ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE), + status); } /* Character set-aware version of str_to_datetime() */ -bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length, - MYSQL_TIME *l_time, ulonglong flags, - MYSQL_TIME_STATUS *status) +bool Temporal::str_to_datetime(MYSQL_TIME_STATUS *status, + const char *str, size_t length, CHARSET_INFO *cs, + date_mode_t flags) { - char cnv[32]; - if ((cs->state & MY_CS_NONASCII) != 0) - { - length= to_ascii(cs, str, length, cnv, sizeof(cnv)); - str= cnv; - } - return str_to_datetime(str, length, l_time, flags, status); + TemporalAsciiBuffer tmp(str, length, cs); + return ::str_to_datetime(tmp.str, tmp.length, this, + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), + status); +} + + +/* Character set-aware version of str_to_DDhhmmssff() */ +bool Interval_DDhhmmssff::str_to_DDhhmmssff(MYSQL_TIME_STATUS *status, + const char *str, size_t length, + CHARSET_INFO *cs, ulong max_hour) +{ + TemporalAsciiBuffer tmp(str, length, cs); + return ::str_to_DDhhmmssff(tmp.str, tmp.length, this, UINT_MAX32, status); } @@ -390,119 +413,73 @@ bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length, See description of str_to_datetime() for more information. */ -bool -str_to_datetime_with_warn(CHARSET_INFO *cs, +static bool +str_to_datetime_with_warn(THD *thd, CHARSET_INFO *cs, const char *str, size_t length, MYSQL_TIME *l_time, - ulonglong flags) + date_mode_t flags, MYSQL_TIME_STATUS *status) { - MYSQL_TIME_STATUS status; - THD *thd= current_thd; - bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status); - if (ret_val || status.warnings) + Temporal_hybrid *t= new(l_time) Temporal_hybrid(status, str, length, cs, flags); + if (!t->is_valid_temporal() || status->warnings) + { + const ErrConvString err(str, length, &my_charset_bin); make_truncated_value_warning(thd, - ret_val ? Sql_condition::WARN_LEVEL_WARN : - Sql_condition::time_warn_level(status.warnings), - str, length, flags & TIME_TIME_ONLY ? + !t->is_valid_temporal() ? + Sql_condition::WARN_LEVEL_WARN : + Sql_condition::time_warn_level(status->warnings), + &err, flags & TIME_TIME_ONLY ? MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS); + } DBUG_EXECUTE_IF("str_to_datetime_warn", push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_YES, str);); - return ret_val; + return !t->is_valid_temporal(); } -/** - converts a pair of numbers (integer part, microseconds) to MYSQL_TIME - - @param neg sign of the time value - @param nr integer part of the number to convert - @param sec_part microsecond part of the number - @param ltime converted value will be written here - @param fuzzydate conversion flags (TIME_INVALID_DATE, etc) - @param str original number, as an ErrConv. For the warning - @param field_name field name or NULL if not a field. For the warning - - @returns 0 for success, 1 for a failure -*/ -static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part, - MYSQL_TIME *ltime, ulonglong fuzzydate, - const ErrConv *str, - const char *field_name) +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) { - int was_cut; - longlong res; - enum_mysql_timestamp_type ts_type; - bool have_warnings; - - if (fuzzydate & TIME_TIME_ONLY) - { - fuzzydate= TIME_TIME_ONLY; // clear other flags - ts_type= MYSQL_TIMESTAMP_TIME; - res= number_to_time(neg, nr, sec_part, ltime, &was_cut); - have_warnings= MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut); - } - else - { - ts_type= MYSQL_TIMESTAMP_DATETIME; - if (neg) - { - res= -1; - } - else - { - res= number_to_datetime(nr, sec_part, ltime, fuzzydate, &was_cut); - have_warnings= was_cut && (fuzzydate & TIME_NO_ZERO_IN_DATE); - } - } - - if (res < 0 || have_warnings) - { - make_truncated_value_warning(current_thd, - Sql_condition::WARN_LEVEL_WARN, str, - res < 0 ? MYSQL_TIMESTAMP_ERROR : ts_type, - field_name); - } - return res < 0; + MYSQL_TIME_STATUS status; + return str_to_datetime_with_warn(thd, cs, str, length, l_time, flags, &status); } -bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, - ulonglong fuzzydate, const char *field_name) +bool double_to_datetime_with_warn(THD *thd, double value, MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *field_name) { const ErrConvDouble str(value); - bool neg= value < 0; - - if (neg) - value= -value; - - if (value > LONGLONG_MAX) - value= static_cast<double>(LONGLONG_MAX); - - longlong nr= static_cast<ulonglong>(floor(value)); - uint sec_part= static_cast<ulong>((value - floor(value))*TIME_SECOND_PART_FACTOR); - return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str, - field_name); + Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, Sec6(value), fuzzydate, + &str, field_name); + return !t->is_valid_temporal(); } -bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime, - ulonglong fuzzydate, const char *field_name) +bool decimal_to_datetime_with_warn(THD *thd, const my_decimal *value, + MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *field_name) { const ErrConvDecimal str(value); - ulonglong nr; - ulong sec_part; - bool neg= my_decimal2seconds(value, &nr, &sec_part); - return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str, - field_name); + Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, Sec6(value), fuzzydate, + &str, field_name); + return !t->is_valid_temporal(); } -bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime, - ulonglong fuzzydate, const char *field_name) +bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr, + MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *field_name) { - const ErrConvInteger str(neg ? - (longlong) value : (longlong) value, !neg); - return number_to_time_with_warn(neg, value, 0, ltime, - fuzzydate, &str, field_name); + const ErrConvInteger str(nr); + /* + Note: conversion from an integer to TIME can overflow to '838:59:59.999999', + so the conversion result can have fractional digits. + */ + Temporal_hybrid *t= new (ltime) + Temporal_hybrid(thd, Sec6(nr), + fuzzydate, &str, field_name); + return !t->is_valid_temporal(); } @@ -549,7 +526,7 @@ void localtime_to_TIME(MYSQL_TIME *to, struct tm *from) } -void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds) +void calc_time_from_sec(MYSQL_TIME *to, ulong seconds, ulong microseconds) { long t_seconds; // to->neg is not cleared, it may already be set to a useful value @@ -932,9 +909,7 @@ void make_truncated_value_warning(THD *thd, timestamp_type time_type, const char *field_name) { - char warn_buff[MYSQL_ERRMSG_SIZE]; const char *type_str; - CHARSET_INFO *cs= &my_charset_latin1; switch (time_type) { case MYSQL_TIMESTAMP_DATE: @@ -948,23 +923,9 @@ void make_truncated_value_warning(THD *thd, type_str= "datetime"; break; } - if (field_name) - cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), - ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), - type_str, sval->ptr(), field_name, - (ulong) thd->get_stmt_da()->current_row_for_warning()); - else - { - if (time_type > MYSQL_TIMESTAMP_ERROR) - cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), - ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), - type_str, sval->ptr()); - else - cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), - ER_THD(thd, ER_WRONG_VALUE), type_str, sval->ptr()); - } - push_warning(thd, level, - ER_TRUNCATED_WRONG_VALUE, warn_buff); + return thd->push_warning_wrong_or_truncated_value(level, + time_type <= MYSQL_TIMESTAMP_ERROR, + type_str, sval->ptr(), field_name); } @@ -975,7 +936,7 @@ void make_truncated_value_warning(THD *thd, (X)->second_part) #define GET_PART(X, N) X % N ## LL; X/= N ## LL -bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, +bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, const INTERVAL &interval) { long period, sign; @@ -1090,7 +1051,6 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, invalid_date: { - THD *thd= current_thd; push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_DATETIME_FUNCTION_OVERFLOW, ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW), @@ -1130,7 +1090,7 @@ null_date: bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, - int l_sign, longlong *seconds_out, long *microseconds_out) + int l_sign, ulonglong *seconds_out, ulong *microseconds_out) { long days; bool neg; @@ -1157,10 +1117,10 @@ calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, } microseconds= ((longlong)days * SECONDS_IN_24H + - (longlong)(l_time1->hour*3600L + + (longlong)(l_time1->hour*3600LL + l_time1->minute*60L + l_time1->second) - - l_sign*(longlong)(l_time2->hour*3600L + + l_sign*(longlong)(l_time2->hour*3600LL + l_time2->minute*60L + l_time2->second)) * 1000000LL + (longlong)l_time1->second_part - @@ -1172,17 +1132,17 @@ calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, microseconds= -microseconds; neg= 1; } - *seconds_out= microseconds/1000000L; - *microseconds_out= (long) (microseconds%1000000L); + *seconds_out= (ulonglong) microseconds/1000000L; + *microseconds_out= (ulong) (microseconds%1000000L); return neg; } bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, - int l_sign, MYSQL_TIME *l_time3, ulonglong fuzzydate) + int l_sign, MYSQL_TIME *l_time3, date_mode_t fuzzydate) { - longlong seconds; - long microseconds; + ulonglong seconds; + ulong microseconds; bzero((char *) l_time3, sizeof(*l_time3)); l_time3->neg= calc_time_diff(l_time1, l_time2, l_sign, &seconds, µseconds); @@ -1201,7 +1161,7 @@ bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, ("invalid" means > TIME_MAX_SECOND) */ set_if_smaller(seconds, INT_MAX32); - calc_time_from_sec(l_time3, (long) seconds, microseconds); + calc_time_from_sec(l_time3, (ulong) seconds, microseconds); return ((fuzzydate & TIME_NO_ZERO_DATE) && (seconds == 0) && (microseconds == 0)); } @@ -1258,56 +1218,6 @@ bool time_to_datetime(MYSQL_TIME *ltime) } -/** - Return a valid DATE or DATETIME value from an arbitrary MYSQL_TIME. - If ltime is TIME, it's first converted to DATETIME. - If ts_type is DATE, hhmmss is set to zero. - The date part of the result is checked against fuzzy_date. - - @param ltime The value to convert. - @param fuzzy_date Flags to check date. - @param ts_type The type to convert to. - @return false on success, true of error (negative time).*/ -bool -make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date, - timestamp_type ts_type) -{ - DBUG_ASSERT(ts_type == MYSQL_TIMESTAMP_DATE || - ts_type == MYSQL_TIMESTAMP_DATETIME); - if (ltime->time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(ltime)) - { - /* e.g. negative time */ - ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - &str, ts_type, 0); - return true; - } - if ((ltime->time_type= ts_type) == MYSQL_TIMESTAMP_DATE) - ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; - return check_date_with_warn(ltime, fuzzy_date, ts_type); -} - - -/* - Convert a TIME value to DAY-TIME interval, e.g. for extraction: - EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc. - Moves full days from ltime->hour to ltime->day. - Note, time_type is set to MYSQL_TIMESTAMP_NONE, to make sure that - the structure is not used for anything else other than extraction: - non-extraction TIME functions expect zero day value! -*/ -void time_to_daytime_interval(MYSQL_TIME *ltime) -{ - DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME); - DBUG_ASSERT(ltime->year == 0); - DBUG_ASSERT(ltime->month == 0); - DBUG_ASSERT(ltime->day == 0); - ltime->day= ltime->hour / 24; - ltime->hour%= 24; - ltime->time_type= MYSQL_TIMESTAMP_NONE; -} - - /*** Conversion from TIME to DATETIME ***/ /* @@ -1335,8 +1245,8 @@ mix_date_and_time_complex(MYSQL_TIME *ldate, const MYSQL_TIME *ltime) { DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE || ldate->time_type == MYSQL_TIMESTAMP_DATETIME); - longlong seconds; - long days, useconds; + ulonglong seconds; + ulong days, useconds; int sign= ltime->neg ? 1 : -1; ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds); @@ -1426,7 +1336,7 @@ time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) bool time_to_datetime_with_warn(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to, - ulonglong fuzzydate) + date_mode_t fuzzydate) { int warn= 0; DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); @@ -1442,34 +1352,13 @@ time_to_datetime_with_warn(THD *thd, check_date(to, fuzzydate, &warn))) { ErrConvTime str(from); - make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, - &str, MYSQL_TIMESTAMP_DATETIME, 0); + thd->push_warning_truncated_wrong_value("datetime", str.ptr()); return true; } return false; } -bool datetime_to_time_with_warn(THD *thd, const MYSQL_TIME *dt, - MYSQL_TIME *tm, uint dec) -{ - if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) - { - *tm= *dt; - datetime_to_time(tm); - return false; - } - else /* new mode */ - { - MYSQL_TIME current_date; - set_current_date(thd, ¤t_date); - calc_time_diff(dt, ¤t_date, 1, tm, 0); - } - int warnings= 0; - return check_time_range(tm, dec, &warnings); -} - - longlong pack_time(const MYSQL_TIME *my_time) { return ((((((my_time->year * 13ULL + @@ -1511,3 +1400,11 @@ void unpack_time(longlong packed, MYSQL_TIME *my_time, break; } } + + +bool my_decimal::to_datetime_with_warn(THD *thd, MYSQL_TIME *to, + date_mode_t fuzzydate, + const char *field_name) +{ + return decimal_to_datetime_with_warn(thd, this, to, fuzzydate, field_name); +} diff --git a/sql/sql_time.h b/sql/sql_time.h index d3607a28a76..433374e2e9a 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -17,6 +17,7 @@ #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 */ @@ -35,69 +36,28 @@ ulong convert_period_to_month(ulong period); ulong convert_month_to_period(ulong month); void set_current_date(THD *thd, MYSQL_TIME *to); bool time_to_datetime(MYSQL_TIME *ltime); -void time_to_daytime_interval(MYSQL_TIME *l_time); 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(CHARSET_INFO *cs, const char *str, size_t length, MYSQL_TIME *l_time, - ulonglong flags); -bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, - ulonglong fuzzydate, +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 char *name); -bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime, - ulonglong fuzzydate, +bool decimal_to_datetime_with_warn(THD *thd, + const my_decimal *value, MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *name); -bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime, - ulonglong fuzzydate, +bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr, + MYSQL_TIME *ltime, + date_mode_t fuzzydate, 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, - ulonglong fuzzydate); -/* - Simply truncate the YYYY-MM-DD part to 0000-00-00 - and change time_type to MYSQL_TIMESTAMP_TIME -*/ -inline void datetime_to_time(MYSQL_TIME *ltime) -{ - DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE || - ltime->time_type == MYSQL_TIMESTAMP_DATETIME); - DBUG_ASSERT(ltime->neg == 0); - ltime->year= ltime->month= ltime->day= 0; - ltime->time_type= MYSQL_TIMESTAMP_TIME; -} - - -/** - Convert DATE/DATETIME to TIME(dec) - using CURRENT_DATE in a non-old mode, - or using simple truncation in old mode (OLD_MODE_ZERO_DATE_TIME_CAST). - - @param thd - the thread to get the variables.old_behaviour value from - @param dt - the DATE of DATETIME value to convert - @param[out] tm - store result here - @param dec - the desired scale. The fractional part of the result - is checked according to this parameter before returning - the conversion result. "dec" is important in the corner - cases near the max/min limits. - If the result is '838:59:59.999999' and the desired scale - is less than 6, an error is returned. - Note, dec is not important in the - OLD_MODE_ZERO_DATE_TIME_CAST old mode. - - - in case of OLD_MODE_ZERO_DATE_TIME_CAST - the TIME part is simply truncated and "false" is returned. - - otherwise, the result is calculated effectively similar to: - TIMEDIFF(dt, CAST(CURRENT_DATE AS DATETIME)) - If the difference fits into the supported TIME range, "false" is returned, - otherwise a warning is issued and "true" is returned. - - @return false - on success - @return true - on error -*/ -bool datetime_to_time_with_warn(THD *, const MYSQL_TIME *dt, - MYSQL_TIME *tm, uint dec); - + date_mode_t fuzzydate); inline void datetime_to_date(MYSQL_TIME *ltime) { @@ -120,14 +80,6 @@ void make_truncated_value_warning(THD *thd, timestamp_type time_type, const char *field_name); -static inline void make_truncated_value_warning(THD *thd, - Sql_condition::enum_warning_level level, const char *str_val, size_t str_length, timestamp_type time_type, - const char *field_name) -{ - const ErrConvString str(str_val, str_length, &my_charset_bin); - make_truncated_value_warning(thd, level, &str, time_type, field_name); -} - extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type, const char *format_str, uint format_length); @@ -138,10 +90,10 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec); /* MYSQL_TIME operations */ -bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, +bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, const INTERVAL &interval); bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, - int l_sign, longlong *seconds_out, long *microseconds_out); + int l_sign, ulonglong *seconds_out, ulong *microseconds_out); int append_interval(String *str, interval_type int_type, const INTERVAL &interval); /** @@ -167,26 +119,17 @@ int append_interval(String *str, interval_type int_type, @return false - otherwise */ bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, - int lsign, MYSQL_TIME *l_time3, ulonglong fuzzydate); + 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, long seconds, long microseconds); -uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year); +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); -/* Character set-aware version of str_to_time() */ -bool str_to_time(CHARSET_INFO *cs, const char *str,size_t length, - MYSQL_TIME *l_time, ulonglong fuzzydate, - MYSQL_TIME_STATUS *status); -/* Character set-aware version of str_to_datetime() */ -bool str_to_datetime(CHARSET_INFO *cs, - const char *str, size_t length, - MYSQL_TIME *l_time, ulonglong flags, - MYSQL_TIME_STATUS *status); /* convenience wrapper */ inline bool parse_date_time_format(timestamp_type format_type, @@ -223,15 +166,14 @@ non_zero_date(const MYSQL_TIME *ltime) non_zero_hhmmssuu(ltime)); } static inline bool -check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut) +check_date(const MYSQL_TIME *ltime, date_mode_t flags, int *was_cut) { - return check_date(ltime, non_zero_date(ltime), flags, 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(const MYSQL_TIME *ltime, ulonglong fuzzy_date, +bool check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, date_mode_t fuzzy_date, timestamp_type ts_type); -bool make_date_with_warn(MYSQL_TIME *ltime, - ulonglong fuzzy_date, timestamp_type ts_type); -bool adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec); +bool adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec); longlong pack_time(const MYSQL_TIME *my_time); void unpack_time(longlong packed, MYSQL_TIME *my_time, diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index dde93dc6ed6..b79c1a1adb1 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -35,16 +35,6 @@ #include "sp_cache.h" // sp_invalidate_cache #include <mysys_err.h> -LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str, - const char* str, size_t length, - MEM_ROOT *mem_root) -{ - if (!(lex_str->str= strmake_root(mem_root, str, length))) - return 0; - lex_str->length= length; - return lex_str; -} - /*************************************************************************/ /** @@ -622,9 +612,10 @@ end: my_ok(thd); DBUG_RETURN(result); - -WSREP_ERROR_LABEL: +#ifdef WITH_WSREP +wsrep_error_label: DBUG_RETURN(true); +#endif } @@ -1502,8 +1493,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db, if (likely((name= error_handler.get_trigger_name()))) { - if (unlikely(!(make_lex_string(&trigger->name, name->str, - name->length, &table->mem_root)))) + trigger->name= safe_lexcstrdup_root(&table->mem_root, *name); + if (unlikely(!trigger->name.str)) goto err_with_lex_cleanup; } trigger->definer= ((!trg_definer || !trg_definer->length) ? @@ -2314,12 +2305,10 @@ void Table_triggers_list::mark_fields_used(trg_event_type event) if (trg_field->field_idx != (uint)-1) { DBUG_PRINT("info", ("marking field: %d", trg_field->field_idx)); - bitmap_set_bit(trigger_table->read_set, trg_field->field_idx); if (trg_field->get_settable_routine_parameter()) bitmap_set_bit(trigger_table->write_set, trg_field->field_idx); - if (trigger_table->field[trg_field->field_idx]->vcol_info) - trigger_table->mark_virtual_col(trigger_table-> - field[trg_field->field_idx]); + trigger_table->mark_column_with_deps( + trigger_table->field[trg_field->field_idx]); } } } diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index bab9bb5e9ac..798e929170c 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -495,7 +495,7 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) bool Sql_cmd_truncate_table::execute(THD *thd) { bool res= TRUE; - TABLE_LIST *table= thd->lex->select_lex.table_list.first; + TABLE_LIST *table= thd->lex->first_select_lex()->table_list.first; DBUG_ENTER("Sql_cmd_truncate_table::execute"); if (check_one_table_access(thd, DROP_ACL, table)) diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index a5085fdfc58..0e4caae7a2f 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -173,7 +173,7 @@ bool get_type_attributes_for_tvc(THD *thd, Item *item; for (uint holder_pos= 0 ; (item= it++); holder_pos++) { - DBUG_ASSERT(item->fixed); + DBUG_ASSERT(item->is_fixed()); holders[holder_pos].add_argument(item); } } @@ -251,7 +251,6 @@ bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl, holders[pos].type_handler(), &holders[pos]/*Type_all_attributes*/, holders[pos].get_maybe_null()); - new_holder->fix_fields(thd, 0); sl->item_list.push_back(new_holder); } @@ -296,7 +295,7 @@ int table_value_constr::save_explain_data_intern(THD *thd, explain->select_id= select_lex->select_number; explain->select_type= select_lex->type; - explain->linkage= select_lex->linkage; + explain->linkage= select_lex->get_linkage(); explain->using_temporary= false; explain->using_filesort= false; /* Setting explain->message means that all other members are invalid */ @@ -564,7 +563,7 @@ bool Item_subselect::wrap_tvc_in_derived_table(THD *thd, Item *item; SELECT_LEX *sq_select; // select for IN subquery; sq_select= lex->current_select; - sq_select->linkage= tvc_sl->linkage; + sq_select->set_linkage(tvc_sl->get_linkage()); sq_select->parsing_place= SELECT_LIST; item= new (thd->mem_root) Item_field(thd, &sq_select->context, NULL, NULL, &star_clex_str); @@ -583,7 +582,7 @@ bool Item_subselect::wrap_tvc_in_derived_table(THD *thd, goto err; tvc_select= lex->current_select; derived_unit= tvc_select->master_unit(); - tvc_select->linkage= DERIVED_TABLE_TYPE; + tvc_select->set_linkage(DERIVED_TABLE_TYPE); lex->current_select= sq_select; @@ -710,7 +709,7 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd, mysql_init_select(lex); tvc_select= lex->current_select; derived_unit= tvc_select->master_unit(); - tvc_select->linkage= DERIVED_TABLE_TYPE; + tvc_select->set_linkage(DERIVED_TABLE_TYPE); /* Create TVC used in the transformation */ if (create_value_list_for_tvc(thd, &values)) diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 22eebaf6a38..23fa678e7cf 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -21,11 +21,13 @@ #include "sql_time.h" #include "item.h" #include "log.h" +#include "tztime.h" Type_handler_row type_handler_row; Type_handler_null type_handler_null; +Type_handler_bool type_handler_bool; Type_handler_tiny type_handler_tiny; Type_handler_short type_handler_short; Type_handler_long type_handler_long; @@ -41,6 +43,7 @@ Type_handler_olddecimal type_handler_olddecimal; Type_handler_newdecimal type_handler_newdecimal; Type_handler_year type_handler_year; +Type_handler_year type_handler_year2; Type_handler_time type_handler_time; Type_handler_date type_handler_date; Type_handler_timestamp type_handler_timestamp; @@ -56,6 +59,7 @@ Type_handler_set type_handler_set; Type_handler_string type_handler_string; Type_handler_var_string type_handler_var_string; Type_handler_varchar type_handler_varchar; +Type_handler_hex_hybrid type_handler_hex_hybrid; static Type_handler_varchar_compressed type_handler_varchar_compressed; Type_handler_tiny_blob type_handler_tiny_blob; @@ -91,6 +95,9 @@ bool Type_handler_data::init() &type_handler_geometry, &type_handler_geometry) || m_type_aggregator_for_result.add(&type_handler_geometry, + &type_handler_hex_hybrid, + &type_handler_long_blob) || + m_type_aggregator_for_result.add(&type_handler_geometry, &type_handler_tiny_blob, &type_handler_long_blob) || m_type_aggregator_for_result.add(&type_handler_geometry, @@ -125,16 +132,437 @@ bool Type_handler_data::init() Type_handler_data *type_handler_data= NULL; -void Time::make_from_item(Item *item, const Options opt) + +void VDec::set(Item *item) +{ + m_ptr= item->val_decimal(&m_buffer); + DBUG_ASSERT((m_ptr == NULL) == item->null_value); +} + + +VDec::VDec(Item *item) +{ + m_ptr= item->val_decimal(&m_buffer); + DBUG_ASSERT((m_ptr == NULL) == item->null_value); +} + + +VDec_op::VDec_op(Item_func_hybrid_field_type *item) +{ + m_ptr= item->decimal_op(&m_buffer); + DBUG_ASSERT((m_ptr == NULL) == item->null_value); +} + + +date_mode_t Temporal::sql_mode_for_dates(THD *thd) +{ + return ::sql_mode_for_dates(thd); +} + + +bool Dec_ptr::to_datetime_with_warn(THD *thd, MYSQL_TIME *to, + date_mode_t fuzzydate, Item *item) +{ + if (to_datetime_with_warn(thd, to, fuzzydate, item->field_name_or_null())) + return item->null_value|= item->make_zero_date(to, fuzzydate); + return item->null_value= false; +} + + +my_decimal *Temporal::to_decimal(my_decimal *to) const +{ + return date2my_decimal(this, to); +} + + +my_decimal *Temporal::bad_to_decimal(my_decimal *to) const +{ + my_decimal_set_zero(to); + return NULL; +} + + +Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate) +{ + if (item->get_date(thd, this, fuzzydate)) + time_type= MYSQL_TIMESTAMP_NONE; +} + + +void Sec6::make_from_decimal(const my_decimal *d) +{ + m_neg= my_decimal2seconds(d, &m_sec, &m_usec); + m_truncated= (m_sec >= LONGLONG_MAX); +} + + +void Sec6::make_from_double(double nr) +{ + if ((m_neg= nr < 0)) + nr= -nr; + if ((m_truncated= nr > (double) LONGLONG_MAX)) + { + m_sec= LONGLONG_MAX; + m_usec= 0; + } + else + { + m_sec= (ulonglong) nr; + m_usec= (ulong) ((nr - floor(nr)) * 1000000); + } +} + + +void Sec6::make_truncated_warning(THD *thd, const char *type_str) const +{ + char buff[1 + MAX_BIGINT_WIDTH + 1 + 6 + 1]; // '-' int '.' frac '\0' + to_string(buff, sizeof(buff)); + thd->push_warning_truncated_wrong_value(type_str, buff); +} + + +bool Sec6::convert_to_mysql_time(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate, const ErrConv *str, + const char *field_name) const +{ + int warn; + bool is_time= bool(fuzzydate & TIME_TIME_ONLY); + const char *typestr= is_time ? "time" : "datetime"; + bool rc= is_time ? to_time(ltime, &warn) : + to_datetime(ltime, fuzzydate, &warn); + if (truncated()) + { + // The value was already truncated at the constructor call time + thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN, + !is_time, typestr, + str->ptr(), field_name); + } + else if (rc || MYSQL_TIME_WARN_HAVE_WARNINGS(warn)) + thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN, + rc, typestr, str->ptr(), + field_name); + else if (MYSQL_TIME_WARN_HAVE_NOTES(warn)) + thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_NOTE, + rc, typestr, str->ptr(), + field_name); + return rc; +} + + +VSec6::VSec6(THD *thd, Item *item, const char *type_str, ulonglong limit) +{ + if (item->decimals == 0) + { // optimize for an important special case + Longlong_hybrid nr(item->val_int(), item->unsigned_flag); + make_from_int(nr); + m_is_null= item->null_value; + if (!m_is_null && m_sec > limit) + { + m_sec= limit; + m_truncated= true; + ErrConvInteger err(nr); + thd->push_warning_truncated_wrong_value(type_str, err.ptr()); + } + } + else if (item->cmp_type() == REAL_RESULT) + { + double nr= item->val_real(); + make_from_double(nr); + m_is_null= item->null_value; + if (!m_is_null && m_sec > limit) + { + m_sec= limit; + m_truncated= true; + } + if (m_truncated) + { + ErrConvDouble err(nr); + thd->push_warning_truncated_wrong_value(type_str, err.ptr()); + } + } + else + { + VDec tmp(item); + (m_is_null= tmp.is_null()) ? reset() : make_from_decimal(tmp.ptr()); + if (!m_is_null && m_sec > limit) + { + m_sec= limit; + m_truncated= true; + } + if (m_truncated) + { + ErrConvDecimal err(tmp.ptr()); + thd->push_warning_truncated_wrong_value(type_str, err.ptr()); + } + } +} + + +Year::Year(longlong value, bool unsigned_flag, uint length) +{ + if ((m_truncated= (value < 0))) // Negative or huge unsigned + m_year= unsigned_flag ? 9999 : 0; + else if (value > 9999) + { + m_truncated= true; + m_year= 9999; + } + else if (length == 2) + { + m_year= value < 70 ? (uint) value + 2000 : + value <= 1900 ? (uint) value + 1900 : + (uint) value; + } + else + m_year= (uint) value; + DBUG_ASSERT(m_year <= 9999); +} + + +uint Year::year_precision(const Item *item) const +{ + return item->type_handler() == &type_handler_year2 ? 2 : 4; +} + + +VYear::VYear(Item *item) + :Year_null(item->to_longlong_null(), item->unsigned_flag, year_precision(item)) +{ } + + +VYear_op::VYear_op(Item_func_hybrid_field_type *item) + :Year_null(item->to_longlong_null_op(), item->unsigned_flag, + year_precision(item)) +{ } + + +const LEX_CSTRING Interval_DDhhmmssff::m_type_name= + {STRING_WITH_LEN("INTERVAL DAY TO SECOND")}; + + +Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, MYSQL_TIME_STATUS *st, + bool push_warnings, + Item *item, ulong max_hour) { - if (item->get_date(this, opt.get_date_flags())) + my_time_status_init(st); + switch (item->cmp_type()) { + case ROW_RESULT: + DBUG_ASSERT(0); + time_type= MYSQL_TIMESTAMP_NONE; + break; + case TIME_RESULT: + { + if (item->get_date(thd, this, TIME_TIME_ONLY)) + time_type= MYSQL_TIMESTAMP_NONE; + else if (time_type != MYSQL_TIMESTAMP_TIME) + { + st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; + push_warning_wrong_or_truncated_value(thd, ErrConvTime(this), + st->warnings); + time_type= MYSQL_TIMESTAMP_NONE; + } + break; + } + case INT_RESULT: + case REAL_RESULT: + case DECIMAL_RESULT: + case STRING_RESULT: + { + StringBuffer<STRING_BUFFER_USUAL_SIZE> tmp; + String *str= item->val_str(&tmp); + if (!str) + time_type= MYSQL_TIMESTAMP_NONE; + else if (str_to_DDhhmmssff(st, str->ptr(), str->length(), str->charset(), + UINT_MAX32)) + { + if (push_warnings) + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + m_type_name.str, + ErrConvString(str).ptr()); + time_type= MYSQL_TIMESTAMP_NONE; + } + else + { + if (hour > max_hour) + { + st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; + time_type= MYSQL_TIMESTAMP_NONE; + } + // Warn if hour or nanosecond truncation happened + if (push_warnings) + push_warning_wrong_or_truncated_value(thd, ErrConvString(str), + st->warnings); + } + } + break; + } + DBUG_ASSERT(is_valid_value_slow()); +} + + +void +Interval_DDhhmmssff::push_warning_wrong_or_truncated_value(THD *thd, + const ErrConv &str, + int warnings) +{ + if (warnings & MYSQL_TIME_WARN_OUT_OF_RANGE) + { + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + m_type_name.str, str.ptr()); + } + else if (MYSQL_TIME_WARN_HAVE_WARNINGS(warnings)) + { + thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_WARN, + m_type_name.str, str.ptr()); + } + else if (MYSQL_TIME_WARN_HAVE_NOTES(warnings)) + { + thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_NOTE, + m_type_name.str, str.ptr()); + } +} + + +uint Interval_DDhhmmssff::fsp(THD *thd, Item *item) +{ + MYSQL_TIME_STATUS st; + switch (item->cmp_type()) { + case INT_RESULT: + case TIME_RESULT: + return item->decimals; + case REAL_RESULT: + case DECIMAL_RESULT: + return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); + case ROW_RESULT: + DBUG_ASSERT(0); + return 0; + case STRING_RESULT: + break; + } + if (!item->const_item() || item->is_expensive()) + return TIME_SECOND_PART_DIGITS; + Interval_DDhhmmssff it(thd, &st, false/*no warnings*/, item, UINT_MAX32); + return it.is_valid_interval_DDhhmmssff() ? st.precision : + TIME_SECOND_PART_DIGITS; +} + + +void Time::make_from_item(THD *thd, int *warn, Item *item, const Options opt) +{ + *warn= 0; + if (item->get_date(thd, this, opt.get_date_flags())) time_type= MYSQL_TIMESTAMP_NONE; else - valid_MYSQL_TIME_to_valid_value(opt); + valid_MYSQL_TIME_to_valid_value(thd, warn, opt); } -void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags) +/** + Create from a DATETIME by subtracting a given number of days, + implementing an optimized version of calc_time_diff(). +*/ +void Time::make_from_datetime_with_days_diff(int *warn, const MYSQL_TIME *from, + long days) +{ + *warn= 0; + DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_DATETIME || + from->time_type == MYSQL_TIMESTAMP_DATE); + long daynr= calc_daynr(from->year, from->month, from->day); + long daydiff= daynr - days; + if (!daynr) // Zero date + { + set_zero_time(this, MYSQL_TIMESTAMP_TIME); + neg= true; + hour= TIME_MAX_HOUR + 1; // to report "out of range" in "warn" + } + else if (daydiff >=0) + { + neg= false; + year= month= day= 0; + hhmmssff_copy(from); + hour+= daydiff * 24; + time_type= MYSQL_TIMESTAMP_TIME; + } + else + { + longlong timediff= ((((daydiff * 24LL + + from->hour) * 60LL + + from->minute) * 60LL + + from->second) * 1000000LL + + from->second_part); + unpack_time(timediff, this, MYSQL_TIMESTAMP_TIME); + if (year || month) + { + *warn|= MYSQL_TIME_WARN_OUT_OF_RANGE; + year= month= day= 0; + hour= TIME_MAX_HOUR + 1; + } + } + // The above code can generate TIME values outside of the valid TIME range. + adjust_time_range_or_invalidate(warn); +} + + +void Time::make_from_datetime_move_day_to_hour(int *warn, + const MYSQL_TIME *from) +{ + *warn= 0; + DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_DATE || + from->time_type == MYSQL_TIMESTAMP_DATETIME); + time_type= MYSQL_TIMESTAMP_TIME; + neg= false; + year= month= day= 0; + hhmmssff_copy(from); + datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(warn, from->year, + from->month, from->day); + adjust_time_range_or_invalidate(warn); +} + + +void Time::make_from_datetime(int *warn, const MYSQL_TIME *from, long curdays) +{ + if (!curdays) + make_from_datetime_move_day_to_hour(warn, from); + else + make_from_datetime_with_days_diff(warn, from, curdays); +} + + +void Time::make_from_time(int *warn, const MYSQL_TIME *from) +{ + DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); + if (from->year || from->month) + make_from_out_of_range(warn); + else + { + *warn= 0; + DBUG_ASSERT(from->day == 0); + *(static_cast<MYSQL_TIME*>(this))= *from; + adjust_time_range_or_invalidate(warn); + } +} + + +Time::Time(int *warn, const MYSQL_TIME *from, long curdays) +{ + switch (from->time_type) { + case MYSQL_TIMESTAMP_NONE: + case MYSQL_TIMESTAMP_ERROR: + make_from_out_of_range(warn); + break; + case MYSQL_TIMESTAMP_DATE: + case MYSQL_TIMESTAMP_DATETIME: + make_from_datetime(warn, from, curdays); + break; + case MYSQL_TIMESTAMP_TIME: + make_from_time(warn, from); + break; + } + DBUG_ASSERT(is_valid_value_slow()); +} + + +void Temporal_with_date::make_from_item(THD *thd, Item *item, date_mode_t flags) { flags&= ~TIME_TIME_ONLY; /* @@ -144,10 +572,10 @@ void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags) In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY and leave it to get_date() to check date. */ - ulonglong time_flag= (item->field_type() == MYSQL_TYPE_TIME && - !(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ? - TIME_TIME_ONLY : 0; - if (item->get_date(this, flags | time_flag)) + date_mode_t time_flag= (item->field_type() == MYSQL_TYPE_TIME && + !(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ? + TIME_TIME_ONLY : date_mode_t(0); + if (item->get_date(thd, this, flags | time_flag)) time_type= MYSQL_TIMESTAMP_NONE; else if (time_type == MYSQL_TIMESTAMP_TIME) { @@ -160,6 +588,72 @@ void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags) } +void Temporal_with_date::make_from_item(THD *thd, Item *item) +{ + return make_from_item(thd, item, sql_mode_for_dates(thd)); +} + + +void Temporal_with_date::check_date_or_invalidate(int *warn, date_mode_t flags) +{ + if (check_date(this, pack_time(this) != 0, + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), warn)) + time_type= MYSQL_TIMESTAMP_NONE; +} + + +void Datetime::make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, + date_mode_t flags) +{ + DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); + if (time_to_datetime(thd, from, this)) + make_from_out_of_range(warn); + else + { + *warn= 0; + check_date_or_invalidate(warn, flags); + } +} + + +void Datetime::make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from, + date_mode_t flags) +{ + DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_DATE || + from->time_type == MYSQL_TIMESTAMP_DATETIME); + if (from->neg || check_datetime_range(from)) + make_from_out_of_range(warn); + else + { + *warn= 0; + *(static_cast<MYSQL_TIME*>(this))= *from; + date_to_datetime(this); + check_date_or_invalidate(warn, flags); + } +} + + +Datetime::Datetime(THD *thd, int *warn, const MYSQL_TIME *from, + date_mode_t flags) +{ + DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false); + switch (from->time_type) { + case MYSQL_TIMESTAMP_ERROR: + case MYSQL_TIMESTAMP_NONE: + make_from_out_of_range(warn); + break; + case MYSQL_TIMESTAMP_TIME: + make_from_time(thd, warn, from, flags); + break; + case MYSQL_TIMESTAMP_DATETIME: + case MYSQL_TIMESTAMP_DATE: + make_from_datetime(thd, warn, from, flags); + break; + } + DBUG_ASSERT(is_valid_value_slow()); +} + + uint Type_std_attributes::count_max_decimals(Item **item, uint nitems) { uint res= 0; @@ -280,6 +774,32 @@ bool Type_std_attributes::count_string_length(const char *func_name, } +/* + Find a handler by its ODBC literal data type. + + @param type_str - data type name, not necessarily 0-terminated + @retval - a pointer to data type handler if type_str points + to a known ODBC literal data type, or NULL otherwise +*/ +const Type_handler * +Type_handler::odbc_literal_type_handler(const LEX_CSTRING *type_str) +{ + if (type_str->length == 1) + { + if (type_str->str[0] == 'd') // {d'2001-01-01'} + return &type_handler_newdate; + else if (type_str->str[0] == 't') // {t'10:20:30'} + return &type_handler_time2; + } + else if (type_str->length == 2) // {ts'2001-01-01 10:20:30'} + { + if (type_str->str[0] == 't' && type_str->str[1] == 's') + return &type_handler_datetime2; + } + return NULL; // Not a known ODBC literal type +} + + /** This method is used by: - Item_user_var_as_out_param::field_type() @@ -433,6 +953,7 @@ const Name Type_handler_string::m_name_char(STRING_WITH_LEN("char")), Type_handler_var_string::m_name_var_string(STRING_WITH_LEN("varchar")), Type_handler_varchar::m_name_varchar(STRING_WITH_LEN("varchar")), + Type_handler_hex_hybrid::m_name_hex_hybrid(STRING_WITH_LEN("hex_hybrid")), Type_handler_tiny_blob::m_name_tinyblob(STRING_WITH_LEN("tinyblob")), Type_handler_medium_blob::m_name_mediumblob(STRING_WITH_LEN("mediumblob")), Type_handler_long_blob::m_name_longblob(STRING_WITH_LEN("longblob")), @@ -443,6 +964,7 @@ const Name Type_handler_set::m_name_set(STRING_WITH_LEN("set")); const Name + Type_handler_bool::m_name_bool(STRING_WITH_LEN("boolean")), Type_handler_tiny::m_name_tiny(STRING_WITH_LEN("tinyint")), Type_handler_short::m_name_short(STRING_WITH_LEN("smallint")), Type_handler_long::m_name_int(STRING_WITH_LEN("int")), @@ -465,6 +987,11 @@ const Name Type_handler_datetime_common::m_name_datetime(STRING_WITH_LEN("datetime")), Type_handler_timestamp_common::m_name_timestamp(STRING_WITH_LEN("timestamp")); +const Name + Type_handler::m_version_default(STRING_WITH_LEN("")), + Type_handler::m_version_mariadb53(STRING_WITH_LEN("mariadb-5.3")), + Type_handler::m_version_mysql56(STRING_WITH_LEN("mysql-5.6")); + const Type_limits_int Type_handler_tiny::m_limits_sint8= Type_limits_sint8(), @@ -1604,6 +2131,70 @@ bool Type_handler_bit:: /*************************************************************************/ +void Type_handler_blob_common:: + Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *def, + const Field *field) const +{ + DBUG_ASSERT(def->key_length == 0); +} + + +void Type_handler_typelib:: + Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *def, + const Field *field) const +{ + DBUG_ASSERT(def->flags & (ENUM_FLAG | SET_FLAG)); + def->interval= field->get_typelib(); +} + + +#ifdef HAVE_SPATIAL +void Type_handler_geometry:: + Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *def, + const Field *field) const +{ + def->geom_type= ((Field_geom*) field)->geom_type; + def->srid= ((Field_geom*) field)->srid; +} +#endif + + +void Type_handler_year:: + Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *def, + const Field *field) const +{ + if (def->length != 4) + { + char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1]; + my_snprintf(buff, sizeof(buff), "YEAR(%llu)", def->length); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_WARN_DEPRECATED_SYNTAX, + ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX), + buff, "YEAR(4)"); + } +} + + +void Type_handler_real_result:: + Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *def, + const Field *field) const +{ + /* + Floating points are stored with FLOATING_POINT_DECIMALS but internally + in MariaDB used with NOT_FIXED_DEC, which is >= FLOATING_POINT_DECIMALS. + */ + if (def->decimals >= FLOATING_POINT_DECIMALS) + def->decimals= NOT_FIXED_DEC; +} + + +/*************************************************************************/ + bool Type_handler:: Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, @@ -2007,8 +2598,8 @@ Field *Type_handler_tiny::make_table_field(const LEX_CSTRING *name, TABLE *table) const { return new (table->in_use->mem_root) - Field_tiny(addr.ptr, attr.max_char_length(), - addr.null_ptr, addr.null_bit, + Field_tiny(addr.ptr(), attr.max_char_length(), + addr.null_ptr(), addr.null_bit(), Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag); } @@ -2020,8 +2611,8 @@ Field *Type_handler_short::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_short(addr.ptr, attr.max_char_length(), - addr.null_ptr, addr.null_bit, + Field_short(addr.ptr(), attr.max_char_length(), + addr.null_ptr(), addr.null_bit(), Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag); } @@ -2032,8 +2623,8 @@ Field *Type_handler_int24::make_table_field(const LEX_CSTRING *name, TABLE *table) const { return new (table->in_use->mem_root) - Field_medium(addr.ptr, attr.max_char_length(), - addr.null_ptr, addr.null_bit, + Field_medium(addr.ptr(), attr.max_char_length(), + addr.null_ptr(), addr.null_bit(), Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag); } @@ -2045,8 +2636,8 @@ Field *Type_handler_long::make_table_field(const LEX_CSTRING *name, TABLE *table) const { return new (table->in_use->mem_root) - Field_long(addr.ptr, attr.max_char_length(), - addr.null_ptr, addr.null_bit, + Field_long(addr.ptr(), attr.max_char_length(), + addr.null_ptr(), addr.null_bit(), Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag); } @@ -2057,8 +2648,8 @@ Field *Type_handler_longlong::make_table_field(const LEX_CSTRING *name, TABLE *table) const { return new (table->in_use->mem_root) - Field_longlong(addr.ptr, attr.max_char_length(), - addr.null_ptr, addr.null_bit, + Field_longlong(addr.ptr(), attr.max_char_length(), + addr.null_ptr(), addr.null_bit(), Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag); } @@ -2070,8 +2661,8 @@ Field *Type_handler_vers_trx_id::make_table_field(const LEX_CSTRING *name, TABLE *table) const { return new (table->in_use->mem_root) - Field_vers_trx_id(addr.ptr, attr.max_char_length(), - addr.null_ptr, addr.null_bit, + Field_vers_trx_id(addr.ptr(), attr.max_char_length(), + addr.null_ptr(), addr.null_bit(), Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag); } @@ -2083,8 +2674,8 @@ Field *Type_handler_float::make_table_field(const LEX_CSTRING *name, TABLE *table) const { return new (table->in_use->mem_root) - Field_float(addr.ptr, attr.max_char_length(), - addr.null_ptr, addr.null_bit, + Field_float(addr.ptr(), attr.max_char_length(), + addr.null_ptr(), addr.null_bit(), Field::NONE, name, (uint8) attr.decimals, 0/*zerofill*/, attr.unsigned_flag); } @@ -2096,8 +2687,8 @@ Field *Type_handler_double::make_table_field(const LEX_CSTRING *name, TABLE *table) const { return new (table->in_use->mem_root) - Field_double(addr.ptr, attr.max_char_length(), - addr.null_ptr, addr.null_bit, + Field_double(addr.ptr(), attr.max_char_length(), + addr.null_ptr(), addr.null_bit(), Field::NONE, name, (uint8) attr.decimals, 0/*zerofill*/, attr.unsigned_flag); } @@ -2118,7 +2709,8 @@ Type_handler_olddecimal::make_table_field(const LEX_CSTRING *name, */ DBUG_ASSERT(0); return new (table->in_use->mem_root) - Field_decimal(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field_decimal(addr.ptr(), attr.max_length, + addr.null_ptr(), addr.null_bit(), Field::NONE, name, (uint8) attr.decimals, 0/*zerofill*/,attr.unsigned_flag); } @@ -2165,7 +2757,7 @@ Type_handler_newdecimal::make_table_field(const LEX_CSTRING *name, len= required_length; } return new (table->in_use->mem_root) - Field_new_decimal(addr.ptr, len, addr.null_ptr, addr.null_bit, + Field_new_decimal(addr.ptr(), len, addr.null_ptr(), addr.null_bit(), Field::NONE, name, dec, 0/*zerofill*/, attr.unsigned_flag); } @@ -2177,7 +2769,8 @@ Field *Type_handler_year::make_table_field(const LEX_CSTRING *name, TABLE *table) const { return new (table->in_use->mem_root) - Field_year(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field_year(addr.ptr(), attr.max_length, + addr.null_ptr(), addr.null_bit(), Field::NONE, name); } @@ -2189,7 +2782,7 @@ Field *Type_handler_null::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_null(addr.ptr, attr.max_length, + Field_null(addr.ptr(), attr.max_length, Field::NONE, name, attr.collation.collation); } @@ -2201,7 +2794,7 @@ Field *Type_handler_timestamp::make_table_field(const LEX_CSTRING *name, { return new_Field_timestamp(table->in_use->mem_root, - addr.ptr, addr.null_ptr, addr.null_bit, + addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, table->s, attr.decimals); } @@ -2217,7 +2810,7 @@ Field *Type_handler_timestamp2::make_table_field(const LEX_CSTRING *name, make_table_field() for make_field() purposes in field.cc. */ return new_Field_timestamp(table->in_use->mem_root, - addr.ptr, addr.null_ptr, addr.null_bit, + addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, table->s, attr.decimals); } @@ -2229,7 +2822,7 @@ Field *Type_handler_newdate::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_newdate(addr.ptr, addr.null_ptr, addr.null_bit, + Field_newdate(addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name); } @@ -2246,7 +2839,7 @@ Field *Type_handler_date::make_table_field(const LEX_CSTRING *name, */ DBUG_ASSERT(0); return new (table->in_use->mem_root) - Field_date(addr.ptr, addr.null_ptr, addr.null_bit, + Field_date(addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name); } @@ -2258,7 +2851,7 @@ Field *Type_handler_time::make_table_field(const LEX_CSTRING *name, { return new_Field_time(table->in_use->mem_root, - addr.ptr, addr.null_ptr, addr.null_bit, + addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, attr.decimals); } @@ -2275,7 +2868,7 @@ Field *Type_handler_time2::make_table_field(const LEX_CSTRING *name, make_table_field() for make_field() purposes in field.cc. */ return new_Field_time(table->in_use->mem_root, - addr.ptr, addr.null_ptr, addr.null_bit, + addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, attr.decimals); } @@ -2287,7 +2880,7 @@ Field *Type_handler_datetime::make_table_field(const LEX_CSTRING *name, { return new_Field_datetime(table->in_use->mem_root, - addr.ptr, addr.null_ptr, addr.null_bit, + addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, attr.decimals); } @@ -2302,7 +2895,7 @@ Field *Type_handler_datetime2::make_table_field(const LEX_CSTRING *name, make_table_field() for make_field() purposes in field.cc. */ return new_Field_datetime(table->in_use->mem_root, - addr.ptr, addr.null_ptr, addr.null_bit, + addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, attr.decimals); } @@ -2314,8 +2907,8 @@ Field *Type_handler_bit::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_bit_as_char(addr.ptr, attr.max_length, - addr.null_ptr, addr.null_bit, + Field_bit_as_char(addr.ptr(), attr.max_length, + addr.null_ptr(), addr.null_bit(), Field::NONE, name); } @@ -2327,7 +2920,8 @@ Field *Type_handler_string::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_string(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field_string(addr.ptr(), attr.max_length, + addr.null_ptr(), addr.null_bit(), Field::NONE, name, attr.collation); } @@ -2339,9 +2933,9 @@ Field *Type_handler_varchar::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_varstring(addr.ptr, attr.max_length, + Field_varstring(addr.ptr(), attr.max_length, HA_VARCHAR_PACKLENGTH(attr.max_length), - addr.null_ptr, addr.null_bit, + addr.null_ptr(), addr.null_bit(), Field::NONE, name, table->s, attr.collation); } @@ -2354,7 +2948,7 @@ Field *Type_handler_tiny_blob::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_blob(addr.ptr, addr.null_ptr, addr.null_bit, + Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, table->s, 1, attr.collation); } @@ -2367,7 +2961,7 @@ Field *Type_handler_blob::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_blob(addr.ptr, addr.null_ptr, addr.null_bit, + Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, table->s, 2, attr.collation); } @@ -2381,7 +2975,7 @@ Type_handler_medium_blob::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_blob(addr.ptr, addr.null_ptr, addr.null_bit, + Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, table->s, 3, attr.collation); } @@ -2394,7 +2988,7 @@ Field *Type_handler_long_blob::make_table_field(const LEX_CSTRING *name, { return new (table->in_use->mem_root) - Field_blob(addr.ptr, addr.null_ptr, addr.null_bit, + Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, table->s, 4, attr.collation); } @@ -2408,7 +3002,7 @@ Field *Type_handler_geometry::make_table_field(const LEX_CSTRING *name, TABLE *table) const { return new (table->in_use->mem_root) - Field_geom(addr.ptr, addr.null_ptr, addr.null_bit, + Field_geom(addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE, name, table->s, 4, (Field::geometry_type) attr.uint_geometry_type(), 0); @@ -2424,7 +3018,8 @@ Field *Type_handler_enum::make_table_field(const LEX_CSTRING *name, TYPELIB *typelib= attr.get_typelib(); DBUG_ASSERT(typelib); return new (table->in_use->mem_root) - Field_enum(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field_enum(addr.ptr(), attr.max_length, + addr.null_ptr(), addr.null_bit(), Field::NONE, name, get_enum_pack_length(typelib->count), typelib, attr.collation); @@ -2440,7 +3035,8 @@ Field *Type_handler_set::make_table_field(const LEX_CSTRING *name, TYPELIB *typelib= attr.get_typelib(); DBUG_ASSERT(typelib); return new (table->in_use->mem_root) - Field_set(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field_set(addr.ptr(), attr.max_length, + addr.null_ptr(), addr.null_bit(), Field::NONE, name, get_enum_pack_length(typelib->count), typelib, attr.collation); @@ -2512,6 +3108,61 @@ uint32 Type_handler_general_purpose_int::max_display_length(const Item *item) /*************************************************************************/ +void Type_handler_row::Item_update_null_value(Item *item) const +{ + DBUG_ASSERT(0); + item->null_value= true; +} + + +void Type_handler_time_common::Item_update_null_value(Item *item) const +{ + MYSQL_TIME ltime; + (void) item->get_date(current_thd, <ime, TIME_TIME_ONLY); +} + + +void Type_handler_temporal_with_date::Item_update_null_value(Item *item) const +{ + MYSQL_TIME ltime; + (void) item->get_date(current_thd, <ime, sql_mode_for_dates(current_thd)); +} + + +void Type_handler_string_result::Item_update_null_value(Item *item) const +{ + StringBuffer<MAX_FIELD_WIDTH> tmp; + (void) item->val_str(&tmp); +} + + +void Type_handler_real_result::Item_update_null_value(Item *item) const +{ + (void) item->val_real(); +} + + +void Type_handler_decimal_result::Item_update_null_value(Item *item) const +{ + my_decimal tmp; + (void) item->val_decimal(&tmp); +} + + +void Type_handler_int_result::Item_update_null_value(Item *item) const +{ + (void) item->val_int(); +} + + +void Type_handler_bool::Item_update_null_value(Item *item) const +{ + (void) item->val_bool(); +} + + +/*************************************************************************/ + int Type_handler_time_common::Item_save_in_field(Item *item, Field *field, bool no_conversions) const { @@ -2704,7 +3355,7 @@ Type_handler_int_result::Item_get_cache(THD *thd, const Item *item) const Item_cache * Type_handler_year::Item_get_cache(THD *thd, const Item *item) const { - return new (thd->mem_root) Item_cache_year(thd); + return new (thd->mem_root) Item_cache_year(thd, item->type_handler()); } Item_cache * @@ -2824,8 +3475,21 @@ bool Type_handler_typelib:: TYPELIB *typelib= NULL; for (uint i= 0; i < nitems; i++) { - if ((typelib= items[i]->get_typelib())) - break; + TYPELIB *typelib2; + if ((typelib2= items[i]->get_typelib())) + { + if (typelib) + { + /* + Two ENUM/SET columns found. We convert such combinations to VARCHAR. + This may change in the future to preserve ENUM/SET + if typelib definitions are equal. + */ + handler->set_handler(&type_handler_varchar); + return func->aggregate_attributes_string(func_name, items, nitems); + } + typelib= typelib2; + } } DBUG_ASSERT(typelib); // There must be at least one typelib func->set_typelib(typelib); @@ -2933,6 +3597,106 @@ bool Type_handler:: } +bool Type_handler_temporal_result:: + Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const +{ + bool rc= Type_handler::Item_func_min_max_fix_attributes(thd, func, + items, nitems); + bool is_time= func->field_type() == MYSQL_TYPE_TIME; + func->decimals= 0; + for (uint i= 0; i < nitems; i++) + { + uint deci= is_time ? items[i]->time_precision(thd) : + items[i]->datetime_precision(thd); + set_if_bigger(func->decimals, deci); + } + + if (rc || func->maybe_null) + return rc; + /* + LEAST/GREATES(non-temporal, temporal) can return NULL. + CAST functions Item_{time|datetime|date}_typecast always set maybe_full + to true. Here we try to detect nullability more thoroughly. + Perhaps CAST functions should also reuse this idea eventually. + */ + const Type_handler *hf= func->type_handler(); + for (uint i= 0; i < nitems; i++) + { + /* + If items[i] does not need conversion to the current temporal data + type, then we trust items[i]->maybe_null, which was already ORred + to func->maybe_null in the argument loop in fix_fields(). + If items[i] requires conversion to the current temporal data type, + then conversion can fail and return NULL even for NOT NULL items. + */ + const Type_handler *ha= items[i]->type_handler(); + if (hf == ha) + continue; // No conversion. + if (ha->cmp_type() != TIME_RESULT) + { + func->maybe_null= true; // Conversion from non-temporal is not safe + break; + } + timestamp_type tf= hf->mysql_timestamp_type(); + timestamp_type ta= ha->mysql_timestamp_type(); + if (tf == ta || + (tf == MYSQL_TIMESTAMP_DATETIME && ta == MYSQL_TIMESTAMP_DATE)) + { + /* + If handlers have the same mysql_timestamp_type(), + then conversion is NULL safe. Conversion from DATE to DATETIME + is also safe. This branch includes data type pairs: + Function return type Argument type Comment + -------------------- ------------- ------------- + TIMESTAMP TIMESTAMP no conversion + TIMESTAMP DATETIME not possible + TIMESTAMP DATE not possible + DATETIME DATETIME no conversion + DATETIME TIMESTAMP safe conversion + DATETIME DATE safe conversion + DATE DATE no conversion + TIME TIME no conversion + + Note, a function cannot return TIMESTAMP if it has non-TIMESTAMP + arguments (it would return DATETIME in such case). + */ + DBUG_ASSERT(hf->field_type() != MYSQL_TYPE_TIMESTAMP || tf == ta); + continue; + } + /* + Here we have the following data type pairs that did not match + the condition above: + + Function return type Argument type Comment + -------------------- ------------- ------- + TIMESTAMP TIME Not possible + DATETIME TIME depends on OLD_MODE_ZERO_DATE_TIME_CAST + DATE TIMESTAMP Not possible + DATE DATETIME Not possible + DATE TIME Not possible + TIME TIMESTAMP Not possible + TIME DATETIME Not possible + TIME DATE Not possible + + Most pairs are not possible, because the function data type + would be DATETIME (according to LEAST/GREATEST aggregation rules). + Conversion to DATETIME from TIME is not safe when + OLD_MODE_ZERO_DATE_TIME_CAST is set: + - negative TIME values cannot be converted to not-NULL DATETIME values + - TIME values can produce DATETIME values that do not pass + NO_ZERO_DATE and NO_ZERO_IN_DATE tests. + */ + DBUG_ASSERT(hf->field_type() == MYSQL_TYPE_DATETIME); + if (!(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) + continue; + func->maybe_null= true; + break; + } + return rc; +} + + bool Type_handler_real_result:: Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, Item **items, uint nitems) const @@ -2983,6 +3747,13 @@ bool Type_handler_int_result:: } +bool Type_handler_bool:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + return Item_sum_hybrid_fix_length_and_dec_numeric(func, &type_handler_bool); +} + + bool Type_handler_real_result:: Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const { @@ -3211,15 +3982,6 @@ bool Type_handler_int_result::Item_val_bool(Item *item) const return item->val_int() != 0; } -bool Type_handler_decimal_result::Item_val_bool(Item *item) const -{ - my_decimal decimal_value; - my_decimal *val= item->val_decimal(&decimal_value); - if (val) - return !my_decimal_is_zero(val); - return false; -} - bool Type_handler_temporal_result::Item_val_bool(Item *item) const { return item->val_real() != 0.0; @@ -3233,43 +3995,42 @@ bool Type_handler_string_result::Item_val_bool(Item *item) const /*************************************************************************/ -bool Type_handler_int_result::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const -{ - return item->get_date_from_int(ltime, fuzzydate); -} - - -bool Type_handler_year::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const +bool Type_handler_int_result::Item_get_date(THD *thd, Item *item, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { - return item->get_date_from_year(ltime, fuzzydate); + return item->get_date_from_int(thd, ltime, fuzzydate); } -bool Type_handler_real_result::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const +bool Type_handler_year::Item_get_date(THD *thd, Item *item, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { - return item->get_date_from_real(ltime, fuzzydate); + return item->null_value= + VYear(item).to_mysql_time_with_warn(thd, ltime, fuzzydate, + item->field_name_or_null()); } -bool Type_handler_decimal_result::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const +bool Type_handler_real_result::Item_get_date(THD *thd, Item *item, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { - return item->get_date_from_decimal(ltime, fuzzydate); + return item->get_date_from_real(thd, ltime, fuzzydate); } -bool Type_handler_string_result::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const +bool Type_handler_string_result::Item_get_date(THD *thd, Item *item, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { - return item->get_date_from_string(ltime, fuzzydate); + return item->get_date_from_string(thd, ltime, fuzzydate); } -bool Type_handler_temporal_result::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const +bool Type_handler_temporal_result::Item_get_date(THD *thd, Item *item, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { DBUG_ASSERT(0); // Temporal type items must implement native get_date() item->null_value= true; @@ -3324,12 +4085,6 @@ longlong Type_handler_int_result:: return item->val_int_unsigned_typecast_from_int(); } -longlong Type_handler_decimal_result:: - Item_val_int_unsigned_typecast(Item *item) const -{ - return item->val_int_unsigned_typecast_from_decimal(); -} - longlong Type_handler_temporal_result:: Item_val_int_unsigned_typecast(Item *item) const { @@ -3390,7 +4145,7 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_str( Item_func_hybrid_field_type *item, String *str) const { - return item->val_str_from_decimal_op(str); + return VDec_op(item).to_string_round(str, item->decimals); } @@ -3399,7 +4154,7 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_real( Item_func_hybrid_field_type *item) const { - return item->val_real_from_decimal_op(); + return VDec_op(item).to_double(); } @@ -3408,7 +4163,7 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_int( Item_func_hybrid_field_type *item) const { - return item->val_int_from_decimal_op(); + return VDec_op(item).to_longlong(item->unsigned_flag); } @@ -3417,17 +4172,30 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *item, my_decimal *dec) const { - return item->val_decimal_from_decimal_op(dec); + return VDec_op(item).to_decimal(dec); } bool Type_handler_decimal_result::Item_func_hybrid_field_type_get_date( + THD *thd, + Item_func_hybrid_field_type *item, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const +{ + return VDec_op(item).to_datetime_with_warn(thd, ltime, fuzzydate, item); +} + + +bool +Type_handler_year::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return item->get_date_from_decimal_op(ltime, fuzzydate); + return item->null_value= + VYear_op(item).to_mysql_time_with_warn(thd, ltime, fuzzydate, NULL); } @@ -3472,11 +4240,12 @@ Type_handler_int_result::Item_func_hybrid_field_type_val_decimal( bool Type_handler_int_result::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return item->get_date_from_int_op(ltime, fuzzydate); + return item->get_date_from_int_op(thd, ltime, fuzzydate); } @@ -3521,11 +4290,12 @@ Type_handler_real_result::Item_func_hybrid_field_type_val_decimal( bool Type_handler_real_result::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return item->get_date_from_real_op(ltime, fuzzydate); + return item->get_date_from_real_op(thd, ltime, fuzzydate); } @@ -3569,11 +4339,12 @@ Type_handler_temporal_result::Item_func_hybrid_field_type_val_decimal( bool Type_handler_temporal_result::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return item->date_op(ltime, fuzzydate); + return item->date_op(thd, ltime, fuzzydate); } @@ -3617,11 +4388,12 @@ Type_handler_time_common::Item_func_hybrid_field_type_val_decimal( bool Type_handler_time_common::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return item->time_op(ltime); + return item->time_op(thd, ltime); } @@ -3665,11 +4437,12 @@ Type_handler_string_result::Item_func_hybrid_field_type_val_decimal( bool Type_handler_string_result::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return item->get_date_from_str_op(ltime, fuzzydate); + return item->get_date_from_str_op(thd, ltime, fuzzydate); } /***************************************************************************/ @@ -3707,10 +4480,16 @@ longlong Type_handler_string_result:: return func->val_int_cmp_string(); } -longlong Type_handler_temporal_result:: +longlong Type_handler_temporal_with_date:: Item_func_between_val_int(Item_func_between *func) const { - return func->val_int_cmp_temporal(); + return func->val_int_cmp_datetime(); +} + +longlong Type_handler_time_common:: + Item_func_between_val_int(Item_func_between *func) const +{ + return func->val_int_cmp_time(); } longlong Type_handler_int_result:: @@ -3930,10 +4709,31 @@ String *Type_handler_string_result:: } -String *Type_handler_temporal_result:: +String *Type_handler_time_common:: + Item_func_min_max_val_str(Item_func_min_max *func, String *str) const +{ + return Time(func).to_string(str, func->decimals); +} + + +String *Type_handler_date_common:: + Item_func_min_max_val_str(Item_func_min_max *func, String *str) const +{ + return Date(func).to_string(str); +} + + +String *Type_handler_datetime_common:: + Item_func_min_max_val_str(Item_func_min_max *func, String *str) const +{ + return Datetime(func).to_string(str, func->decimals); +} + + +String *Type_handler_timestamp_common:: Item_func_min_max_val_str(Item_func_min_max *func, String *str) const { - return func->val_string_from_date(str); + return Datetime(func).to_string(str, func->decimals); } @@ -3947,7 +4747,7 @@ String *Type_handler_int_result:: String *Type_handler_decimal_result:: Item_func_min_max_val_str(Item_func_min_max *func, String *str) const { - return func->val_string_from_decimal(str); + return VDec(func).to_string_round(str, func->decimals); } @@ -3965,13 +4765,30 @@ double Type_handler_string_result:: } -double Type_handler_temporal_result:: +double Type_handler_time_common:: Item_func_min_max_val_real(Item_func_min_max *func) const { - MYSQL_TIME ltime; - if (func->get_date(<ime, 0)) - return 0; - return TIME_to_double(<ime); + return Time(current_thd, func).to_double(); +} + + +double Type_handler_date_common:: + Item_func_min_max_val_real(Item_func_min_max *func) const +{ + return Date(current_thd, func).to_double(); +} + + +double Type_handler_datetime_common:: + Item_func_min_max_val_real(Item_func_min_max *func) const +{ + return Datetime(current_thd, func).to_double(); +} + +double Type_handler_timestamp_common:: + Item_func_min_max_val_real(Item_func_min_max *func) const +{ + return Datetime(current_thd, func).to_double(); } @@ -3989,13 +4806,31 @@ longlong Type_handler_string_result:: } -longlong Type_handler_temporal_result:: +longlong Type_handler_time_common:: Item_func_min_max_val_int(Item_func_min_max *func) const { - MYSQL_TIME ltime; - if (func->get_date(<ime, 0)) - return 0; - return TIME_to_ulonglong(<ime); + return Time(current_thd, func).to_longlong(); +} + + +longlong Type_handler_date_common:: + Item_func_min_max_val_int(Item_func_min_max *func) const +{ + return Date(current_thd, func).to_longlong(); +} + + +longlong Type_handler_datetime_common:: + Item_func_min_max_val_int(Item_func_min_max *func) const +{ + return Datetime(current_thd, func).to_longlong(); +} + + +longlong Type_handler_timestamp_common:: + Item_func_min_max_val_int(Item_func_min_max *func) const +{ + return Datetime(current_thd, func).to_longlong(); } @@ -4022,20 +4857,41 @@ my_decimal *Type_handler_numeric:: } -my_decimal *Type_handler_temporal_result:: +my_decimal *Type_handler_time_common:: Item_func_min_max_val_decimal(Item_func_min_max *func, my_decimal *dec) const { - MYSQL_TIME ltime; - if (func->get_date(<ime, 0)) - return 0; - return date2my_decimal(<ime, dec); + return Time(current_thd, func).to_decimal(dec); +} + + +my_decimal *Type_handler_date_common:: + Item_func_min_max_val_decimal(Item_func_min_max *func, + my_decimal *dec) const +{ + return Date(current_thd, func).to_decimal(dec); +} + + +my_decimal *Type_handler_datetime_common:: + Item_func_min_max_val_decimal(Item_func_min_max *func, + my_decimal *dec) const +{ + return Datetime(current_thd, func).to_decimal(dec); +} + + +my_decimal *Type_handler_timestamp_common:: + Item_func_min_max_val_decimal(Item_func_min_max *func, + my_decimal *dec) const +{ + return Datetime(current_thd, func).to_decimal(dec); } bool Type_handler_string_result:: - Item_func_min_max_get_date(Item_func_min_max *func, - MYSQL_TIME *ltime, ulonglong fuzzydate) const + Item_func_min_max_get_date(THD *thd, Item_func_min_max *func, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const { /* just like ::val_int() method of a string item can be called, @@ -4043,30 +4899,42 @@ bool Type_handler_string_result:: ::get_date() can be called for non-temporal values, for example, SELECT MONTH(GREATEST("2011-11-21", "2010-10-09")) */ - return func->get_date_from_string(ltime, fuzzydate); + return func->get_date_from_string(thd, ltime, fuzzydate); } bool Type_handler_numeric:: - Item_func_min_max_get_date(Item_func_min_max *func, - MYSQL_TIME *ltime, ulonglong fuzzydate) const + Item_func_min_max_get_date(THD *thd, Item_func_min_max *func, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const { - return Item_get_date(func, ltime, fuzzydate); + return Item_get_date(thd, func, ltime, fuzzydate); } bool Type_handler_temporal_result:: - Item_func_min_max_get_date(Item_func_min_max *func, - MYSQL_TIME *ltime, ulonglong fuzzydate) const + Item_func_min_max_get_date(THD *thd, Item_func_min_max *func, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const { - return func->get_date_native(ltime, fuzzydate); + /* + - If the caller specified TIME_TIME_ONLY, then it's going to convert + a DATETIME or DATE to TIME. So we pass the default flags for date. This is + exactly the same with what Item_func_min_max_val_{int|real|decimal|str} or + Item_send_datetime() do. We return the value in accordance with the + current session date flags and let the caller further convert it to TIME. + - If the caller did not specify TIME_TIME_ONLY, then return the value + according to the flags supplied by the caller. + */ + return func->get_date_native(thd, ltime, + fuzzydate & TIME_TIME_ONLY ? + sql_mode_for_dates(thd) : + fuzzydate); } bool Type_handler_time_common:: - Item_func_min_max_get_date(Item_func_min_max *func, - MYSQL_TIME *ltime, ulonglong fuzzydate) const + Item_func_min_max_get_date(THD *thd, Item_func_min_max *func, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const { - return func->get_time_native(ltime); + return func->get_time_native(thd, ltime); } /***************************************************************************/ @@ -4531,7 +5399,7 @@ bool Type_handler:: Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const { uint dec= item->decimals == NOT_FIXED_DEC ? - item->arguments()[0]->time_precision() : + item->arguments()[0]->time_precision(current_thd) : item->decimals; item->fix_attributes_temporal(MIN_TIME_WIDTH, dec); item->maybe_null= true; @@ -4553,7 +5421,7 @@ bool Type_handler:: const { uint dec= item->decimals == NOT_FIXED_DEC ? - item->arguments()[0]->datetime_precision() : + item->arguments()[0]->datetime_precision(current_thd) : item->decimals; item->fix_attributes_temporal(MAX_DATETIME_WIDTH, dec); item->maybe_null= true; @@ -4873,32 +5741,34 @@ bool Type_handler_string_result:: /***************************************************************************/ -uint Type_handler::Item_time_precision(Item *item) const +uint Type_handler::Item_time_precision(THD *thd, Item *item) const { return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); } -uint Type_handler::Item_datetime_precision(Item *item) const +uint Type_handler::Item_datetime_precision(THD *thd, Item *item) const { return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); } -uint Type_handler_string_result::Item_temporal_precision(Item *item, +uint Type_handler_string_result::Item_temporal_precision(THD *thd, Item *item, bool is_time) const { - MYSQL_TIME ltime; StringBuffer<64> buf; String *tmp; MYSQL_TIME_STATUS status; - DBUG_ASSERT(item->fixed); + DBUG_ASSERT(item->is_fixed()); if ((tmp= item->val_str(&buf)) && - !(is_time ? - str_to_time(tmp->charset(), tmp->ptr(), tmp->length(), - <ime, TIME_TIME_ONLY, &status) : - str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(), - <ime, TIME_FUZZY_DATES, &status))) + (is_time ? + Time(thd, &status, tmp->ptr(), tmp->length(), tmp->charset(), + Time::Options(TIME_TIME_ONLY, + Time::DATETIME_TO_TIME_YYYYMMDD_TRUNCATE)). + is_valid_time() : + Datetime(&status, tmp->ptr(), tmp->length(), tmp->charset(), + TIME_FUZZY_DATES). + is_valid_datetime())) return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS); return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); } @@ -5087,7 +5957,7 @@ bool Type_handler::check_null(const Item *item, st_value *value) const bool Type_handler_null:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_NULL; return true; @@ -5095,7 +5965,7 @@ bool Type_handler_null:: bool Type_handler_row:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { DBUG_ASSERT(0); value->m_type= DYN_COL_NULL; @@ -5104,7 +5974,7 @@ bool Type_handler_row:: bool Type_handler_int_result:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= item->unsigned_flag ? DYN_COL_UINT : DYN_COL_INT; value->value.m_longlong= item->val_int(); @@ -5113,7 +5983,7 @@ bool Type_handler_int_result:: bool Type_handler_real_result:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_DOUBLE; value->value.m_double= item->val_real(); @@ -5122,7 +5992,7 @@ bool Type_handler_real_result:: bool Type_handler_decimal_result:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_DECIMAL; my_decimal *dec= item->val_decimal(&value->m_decimal); @@ -5133,7 +6003,7 @@ bool Type_handler_decimal_result:: bool Type_handler_string_result:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_STRING; String *str= item->val_str(&value->m_string); @@ -5144,19 +6014,19 @@ bool Type_handler_string_result:: bool Type_handler_temporal_with_date:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_DATETIME; - item->get_date(&value->value.m_time, sql_mode_for_dates(current_thd)); + item->get_date(thd, &value->value.m_time, sql_mode_for_dates(thd)); return check_null(item, value); } bool Type_handler_time_common:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_DATETIME; - item->get_time(&value->value.m_time); + item->get_time(thd, &value->value.m_time); return check_null(item, value); } @@ -5340,7 +6210,8 @@ bool Type_handler:: bool Type_handler:: Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const { - item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd)); + item->get_date(protocol->thd, &buf->value.m_time, + sql_mode_for_dates(protocol->thd)); if (!item->null_value) return protocol->store(&buf->value.m_time, item->decimals); return protocol->store_null(); @@ -5350,7 +6221,8 @@ bool Type_handler:: bool Type_handler:: Item_send_date(Item *item, Protocol *protocol, st_value *buf) const { - item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd)); + item->get_date(protocol->thd, &buf->value.m_time, + sql_mode_for_dates(protocol->thd)); if (!item->null_value) return protocol->store_date(&buf->value.m_time); return protocol->store_null(); @@ -5360,7 +6232,7 @@ bool Type_handler:: bool Type_handler:: Item_send_time(Item *item, Protocol *protocol, st_value *buf) const { - item->get_time(&buf->value.m_time); + item->get_time(protocol->thd, &buf->value.m_time); if (!item->null_value) return protocol->store_time(&buf->value.m_time, item->decimals); return protocol->store_null(); @@ -5393,11 +6265,10 @@ Item *Type_handler_real_result:: Item *Type_handler_decimal_result:: make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const { - my_decimal decimal_value; - my_decimal *result= item->val_decimal(&decimal_value); - if (item->null_value) + VDec result(item); + if (result.is_null()) return new (thd->mem_root) Item_null(thd, item->name.str); - return new (thd->mem_root) Item_decimal(thd, item->name.str, result, + return new (thd->mem_root) Item_decimal(thd, item->name.str, result.ptr(), item->max_length, item->decimals); } @@ -5420,7 +6291,7 @@ Item *Type_handler_time_common:: make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const { Item_cache_temporal *cache; - longlong value= item->val_time_packed(); + longlong value= item->val_time_packed(thd); if (item->null_value) return new (thd->mem_root) Item_null(thd, item->name.str); cache= new (thd->mem_root) Item_cache_time(thd); @@ -5434,7 +6305,7 @@ Item *Type_handler_temporal_with_date:: make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const { Item_cache_temporal *cache; - longlong value= item->val_datetime_packed(); + longlong value= item->val_datetime_packed(thd); if (item->null_value) return new (thd->mem_root) Item_null(thd, item->name.str); cache= new (thd->mem_root) Item_cache_datetime(thd); @@ -5787,6 +6658,490 @@ void Type_handler_geometry::Item_param_set_param_func(Item_param *param, /***************************************************************************/ +Field *Type_handler_row:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + DBUG_ASSERT(attr->length == 0); + DBUG_ASSERT(f_maybe_null(attr->pack_flag)); + return new (mem_root) Field_row(rec.ptr(), name); +} + + +Field *Type_handler_olddecimal:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_decimal(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + f_decimals(attr->pack_flag), + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag) == 0); +} + + +Field *Type_handler_newdecimal:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_new_decimal(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + f_decimals(attr->pack_flag), + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag) == 0); +} + + +Field *Type_handler_float:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + int decimals= f_decimals(attr->pack_flag); + if (decimals == FLOATING_POINT_DECIMALS) + decimals= NOT_FIXED_DEC; + return new (mem_root) + Field_float(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, decimals, + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag)== 0); +} + + +Field *Type_handler_double:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + int decimals= f_decimals(attr->pack_flag); + if (decimals == FLOATING_POINT_DECIMALS) + decimals= NOT_FIXED_DEC; + return new (mem_root) + Field_double(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, decimals, + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag)== 0); +} + + +Field *Type_handler_tiny:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_tiny(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag) == 0); +} + + +Field *Type_handler_short:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_short(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag) == 0); +} + + +Field *Type_handler_int24:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_medium(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag) == 0); +} + + +Field *Type_handler_long:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_long(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag) == 0); +} + + +Field *Type_handler_longlong:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG)) + return new (mem_root) + Field_vers_trx_id(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag) == 0); + return new (mem_root) + Field_longlong(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + f_is_zerofill(attr->pack_flag) != 0, + f_is_dec(attr->pack_flag) == 0); +} + + +Field *Type_handler_timestamp:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new_Field_timestamp(mem_root, + rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, share, + attr->temporal_dec(MAX_DATETIME_WIDTH)); +} + + +Field *Type_handler_timestamp2:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_timestampf(rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, + name, share, attr->temporal_dec(MAX_DATETIME_WIDTH)); +} + + +Field *Type_handler_year:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_year(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name); +} + + +Field *Type_handler_date:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_date(rec.ptr(),rec.null_ptr(),rec.null_bit(), + attr->unireg_check, name); +} + + +Field *Type_handler_newdate:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_newdate(rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name); +} + + +Field *Type_handler_time:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new_Field_time(mem_root, rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + attr->temporal_dec(MIN_TIME_WIDTH)); +} + + +Field *Type_handler_time2:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_timef(rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + attr->temporal_dec(MIN_TIME_WIDTH)); +} + + +Field *Type_handler_datetime:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new_Field_datetime(mem_root, rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + attr->temporal_dec(MAX_DATETIME_WIDTH)); +} + + +Field *Type_handler_datetime2:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_datetimef(rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, + attr->temporal_dec(MAX_DATETIME_WIDTH)); +} + + +Field *Type_handler_null:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_null(rec.ptr(), (uint32) attr->length, attr->unireg_check, + name, attr->charset); +} + + +Field *Type_handler_bit:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return f_bit_as_char(attr->pack_flag) ? + new (mem_root) Field_bit_as_char(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name) : + new (mem_root) Field_bit(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + bit.ptr(), bit.offs(), attr->unireg_check, name); +} + + +#ifdef HAVE_SPATIAL +Field *Type_handler_geometry:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + status_var_increment(current_thd->status_var.feature_gis); + return new (mem_root) + Field_geom(rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, share, + attr->pack_flag_to_pack_length(), attr->geom_type, attr->srid); +} +#endif + + +Field *Type_handler_string:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_string(rec.ptr(), (uint32) attr->length, + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, attr->charset); +} + + +Field *Type_handler_varchar:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + if (attr->unireg_check == Field::TMYSQL_COMPRESSED) + return new (mem_root) + Field_varstring_compressed(rec.ptr(), (uint32) attr->length, + HA_VARCHAR_PACKLENGTH((uint32) attr->length), + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, share, attr->charset, + zlib_compression_method); + return new (mem_root) + Field_varstring(rec.ptr(), (uint32) attr->length, + HA_VARCHAR_PACKLENGTH((uint32) attr->length), + rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, share, attr->charset); +} + + +Field *Type_handler_blob_common:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + if (attr->unireg_check == Field::TMYSQL_COMPRESSED) + return new (mem_root) + Field_blob_compressed(rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, share, + attr->pack_flag_to_pack_length(), attr->charset, + zlib_compression_method); + return new (mem_root) + Field_blob(rec.ptr(), rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, share, + attr->pack_flag_to_pack_length(), attr->charset); +} + + +Field *Type_handler_enum:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_enum(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, attr->pack_flag_to_pack_length(), + attr->interval, attr->charset); +} + + +Field *Type_handler_set:: + make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const +{ + return new (mem_root) + Field_set(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(), + attr->unireg_check, name, attr->pack_flag_to_pack_length(), + attr->interval, attr->charset); +} + + +/***************************************************************************/ + +void Type_handler:: + Column_definition_attributes_frm_pack(const Column_definition_attributes *def, + uchar *buff) const +{ + def->frm_pack_basic(buff); + def->frm_pack_charset(buff); +} + + +#ifdef HAVE_SPATIAL +void Type_handler_geometry:: + Column_definition_attributes_frm_pack(const Column_definition_attributes *def, + uchar *buff) const +{ + def->frm_pack_basic(buff); + buff[11]= 0; + buff[14]= (uchar) def->geom_type; +} +#endif + + +/***************************************************************************/ + +bool Type_handler:: + Column_definition_attributes_frm_unpack(Column_definition_attributes *attr, + TABLE_SHARE *share, + const uchar *buffer, + LEX_CUSTRING *gis_options) + const +{ + attr->frm_unpack_basic(buffer); + return attr->frm_unpack_charset(share, buffer); +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Column_definition_attributes_frm_unpack(Column_definition_attributes *attr, + TABLE_SHARE *share, + const uchar *buffer, + LEX_CUSTRING *gis_options) + const +{ + uint gis_opt_read, gis_length, gis_decimals; + Field_geom::storage_type st_type; + attr->frm_unpack_basic(buffer); + // charset and geometry_type share the same byte in frm + attr->geom_type= (Field::geometry_type) buffer[14]; + gis_opt_read= gis_field_options_read(gis_options->str, + gis_options->length, + &st_type, &gis_length, + &gis_decimals, &attr->srid); + gis_options->str+= gis_opt_read; + gis_options->length-= gis_opt_read; + return false; +} +#endif + +/***************************************************************************/ + bool Type_handler::Vers_history_point_resolve_unit(THD *thd, Vers_history_point *point) const @@ -5845,3 +7200,364 @@ bool Type_handler_general_purpose_string:: } /***************************************************************************/ + +bool Type_handler_null::Item_const_eq(const Item_const *a, + const Item_const *b, + bool binary_cmp) const +{ + return true; +} + + +bool Type_handler_real_result::Item_const_eq(const Item_const *a, + const Item_const *b, + bool binary_cmp) const +{ + const double *va= a->const_ptr_double(); + const double *vb= b->const_ptr_double(); + return va[0] == vb[0]; +} + + +bool Type_handler_int_result::Item_const_eq(const Item_const *a, + const Item_const *b, + bool binary_cmp) const +{ + const longlong *va= a->const_ptr_longlong(); + const longlong *vb= b->const_ptr_longlong(); + bool res= va[0] == vb[0] && + (va[0] >= 0 || + (a->get_type_all_attributes_from_const()->unsigned_flag == + b->get_type_all_attributes_from_const()->unsigned_flag)); + return res; +} + + +bool Type_handler_string_result::Item_const_eq(const Item_const *a, + const Item_const *b, + bool binary_cmp) const +{ + const String *sa= a->const_ptr_string(); + const String *sb= b->const_ptr_string(); + return binary_cmp ? sa->bin_eq(sb) : + a->get_type_all_attributes_from_const()->collation.collation == + b->get_type_all_attributes_from_const()->collation.collation && + sa->eq(sb, a->get_type_all_attributes_from_const()->collation.collation); +} + + +bool +Type_handler_decimal_result::Item_const_eq(const Item_const *a, + const Item_const *b, + bool binary_cmp) const +{ + const my_decimal *da= a->const_ptr_my_decimal(); + const my_decimal *db= b->const_ptr_my_decimal(); + return !da->cmp(db) && + (!binary_cmp || + a->get_type_all_attributes_from_const()->decimals == + b->get_type_all_attributes_from_const()->decimals); +} + + +bool +Type_handler_temporal_result::Item_const_eq(const Item_const *a, + const Item_const *b, + bool binary_cmp) const +{ + const MYSQL_TIME *ta= a->const_ptr_mysql_time(); + const MYSQL_TIME *tb= b->const_ptr_mysql_time(); + return !my_time_compare(ta, tb) && + (!binary_cmp || + a->get_type_all_attributes_from_const()->decimals == + b->get_type_all_attributes_from_const()->decimals); +} + +/***************************************************************************/ + +const Type_handler * +Type_handler_hex_hybrid::cast_to_int_type_handler() const +{ + return &type_handler_longlong; +} + + +const Type_handler * +Type_handler_hex_hybrid::type_handler_for_system_time() const +{ + return &type_handler_longlong; +} + + +/***************************************************************************/ + +bool Type_handler_row::Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const +{ + DBUG_ASSERT(0); + return false; +} + + +bool Type_handler_int_result::Item_eq_value(THD *thd, + const Type_cmp_attributes *attr, + Item *a, Item *b) const +{ + longlong value0= a->val_int(); + longlong value1= b->val_int(); + return !a->null_value && !b->null_value && value0 == value1 && + (value0 >= 0 || a->unsigned_flag == b->unsigned_flag); +} + + +bool Type_handler_real_result::Item_eq_value(THD *thd, + const Type_cmp_attributes *attr, + Item *a, Item *b) const +{ + double value0= a->val_real(); + double value1= b->val_real(); + return !a->null_value && !b->null_value && value0 == value1; +} + + +bool Type_handler_time_common::Item_eq_value(THD *thd, + const Type_cmp_attributes *attr, + Item *a, Item *b) const +{ + longlong value0= a->val_time_packed(thd); + longlong value1= b->val_time_packed(thd); + return !a->null_value && !b->null_value && value0 == value1; +} + + +bool Type_handler_temporal_with_date::Item_eq_value(THD *thd, + const Type_cmp_attributes *attr, + Item *a, Item *b) const +{ + longlong value0= a->val_datetime_packed(thd); + longlong value1= b->val_datetime_packed(thd); + return !a->null_value && !b->null_value && value0 == value1; +} + + +bool Type_handler_string_result::Item_eq_value(THD *thd, + const Type_cmp_attributes *attr, + Item *a, Item *b) const +{ + String *va, *vb; + StringBuffer<128> cmp_value1, cmp_value2; + return (va= a->val_str(&cmp_value1)) && + (vb= b->val_str(&cmp_value2)) && + va->eq(vb, attr->compare_collation()); +} + + +/***************************************************************************/ + +void Type_handler_var_string:: + Column_definition_implicit_upgrade(Column_definition *c) const +{ + // Change old VARCHAR to new VARCHAR + c->set_handler(&type_handler_varchar); +} + + +void Type_handler_time_common:: + Column_definition_implicit_upgrade(Column_definition *c) const +{ + if (opt_mysql56_temporal_format) + c->set_handler(&type_handler_time2); + else + c->set_handler(&type_handler_time); +} + + +void Type_handler_datetime_common:: + Column_definition_implicit_upgrade(Column_definition *c) const +{ + if (opt_mysql56_temporal_format) + c->set_handler(&type_handler_datetime2); + else + c->set_handler(&type_handler_datetime); +} + + +void Type_handler_timestamp_common:: + Column_definition_implicit_upgrade(Column_definition *c) const +{ + if (opt_mysql56_temporal_format) + c->set_handler(&type_handler_timestamp2); + else + c->set_handler(&type_handler_timestamp); +} + + +/***************************************************************************/ + + +int Type_handler_temporal_with_date::stored_field_cmp_to_item(THD *thd, + Field *field, + Item *item) const +{ + MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time; + field->get_date(&field_time, TIME_INVALID_DATES); + item->get_date(thd, &item_time, TIME_INVALID_DATES); + if (item_time.time_type == MYSQL_TIMESTAMP_TIME && + time_to_datetime(thd, &item_time, item_time_cmp= &item_time2)) + return 1; + return my_time_compare(&field_time, item_time_cmp); +} + + +int Type_handler_time_common::stored_field_cmp_to_item(THD *thd, + Field *field, + Item *item) const +{ + MYSQL_TIME field_time, item_time; + field->get_time(&field_time); + item->get_time(thd, &item_time); + return my_time_compare(&field_time, &item_time); +} + + +int Type_handler_string_result::stored_field_cmp_to_item(THD *thd, + Field *field, + Item *item) const +{ + StringBuffer<MAX_FIELD_WIDTH> item_tmp; + StringBuffer<MAX_FIELD_WIDTH> field_tmp; + String *item_result= item->val_str(&item_tmp); + /* + Some implementations of Item::val_str(String*) actually modify + the field Item::null_value, hence we can't check it earlier. + */ + if (item->null_value) + return 0; + String *field_result= field->val_str(&field_tmp); + return sortcmp(field_result, item_result, field->charset()); +} + + +int Type_handler_int_result::stored_field_cmp_to_item(THD *thd, + Field *field, + Item *item) const +{ + DBUG_ASSERT(0); // Not used yet + return 0; +} + + +int Type_handler_real_result::stored_field_cmp_to_item(THD *thd, + Field *field, + Item *item) const +{ + /* + The patch for Bug#13463415 started using this function for comparing + BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode. + Prefixing the auto variables with volatile fixes the problem.... + */ + volatile double result= item->val_real(); + if (item->null_value) + return 0; + volatile double field_result= field->val_real(); + if (field_result < result) + return -1; + else if (field_result > result) + return 1; + return 0; +} + + +/***************************************************************************/ + + +static bool have_important_literal_warnings(const MYSQL_TIME_STATUS *status) +{ + return (status->warnings & ~MYSQL_TIME_NOTE_TRUNCATED) != 0; +} + + +static void literal_warn(THD *thd, const Item *item, + const char *str, size_t length, CHARSET_INFO *cs, + timestamp_type time_type, + const MYSQL_TIME_STATUS *st, + const char *typestr, bool send_error) +{ + if (likely(item)) + { + if (st->warnings) // e.g. a note on nanosecond truncation + { + ErrConvString err(str, length, cs); + make_truncated_value_warning(thd, + Sql_condition::time_warn_level(st->warnings), + &err, time_type, 0); + } + } + else if (send_error) + { + ErrConvString err(str, length, cs); + my_error(ER_WRONG_VALUE, MYF(0), typestr, err.ptr()); + } +} + + +Item_literal * +Type_handler_date_common::create_literal_item(THD *thd, + const char *str, + size_t length, + CHARSET_INFO *cs, + bool send_error) const +{ + MYSQL_TIME_STATUS st; + Item_literal *item= NULL; + Temporal_hybrid tmp(&st, str, length, cs, sql_mode_for_dates(thd)); + if (tmp.is_valid_temporal() && + tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATE && + !have_important_literal_warnings(&st)) + item= new (thd->mem_root) Item_date_literal(thd, tmp.get_mysql_time()); + literal_warn(thd, item, str, length, cs, MYSQL_TIMESTAMP_DATE, + &st, "DATE", send_error); + return item; +} + + +Item_literal * +Type_handler_temporal_with_date::create_literal_item(THD *thd, + const char *str, + size_t length, + CHARSET_INFO *cs, + bool send_error) const +{ + MYSQL_TIME_STATUS st; + Item_literal *item= NULL; + Temporal_hybrid tmp(&st, str, length, cs, sql_mode_for_dates(thd)); + if (tmp.is_valid_temporal() && + tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATETIME && + !have_important_literal_warnings(&st)) + item= new (thd->mem_root) Item_datetime_literal(thd, tmp.get_mysql_time(), + st.precision); + literal_warn(thd, item, str, length, cs, MYSQL_TIMESTAMP_DATETIME, + &st, "DATETIME", send_error); + return item; +} + + +Item_literal * +Type_handler_time_common::create_literal_item(THD *thd, + const char *str, + size_t length, + CHARSET_INFO *cs, + bool send_error) const +{ + MYSQL_TIME_STATUS st; + Item_literal *item= NULL; + Time::Options opt(TIME_TIME_ONLY, Time::DATETIME_TO_TIME_DISALLOW); + Time tmp(thd, &st, str, length, cs, opt); + if (tmp.is_valid_time() && + !have_important_literal_warnings(&st)) + item= new (thd->mem_root) Item_time_literal(thd, tmp.get_mysql_time(), + st.precision); + literal_warn(thd, item, str, length, cs, MYSQL_TIMESTAMP_TIME, + &st, "TIME", send_error); + return item; +} diff --git a/sql/sql_type.h b/sql/sql_type.h index 1ddcef2da61..fd86102c504 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -28,7 +28,10 @@ class Field; class Column_definition; +class Column_definition_attributes; class Item; +class Item_const; +class Item_literal; class Item_param; class Item_cache; class Item_func_or_sum; @@ -74,6 +77,656 @@ struct TABLE; struct SORT_FIELD_ATTR; class Vers_history_point; +#define my_charset_numeric my_charset_latin1 + +enum scalar_comparison_op +{ + SCALAR_CMP_EQ, + SCALAR_CMP_EQUAL, + SCALAR_CMP_LT, + SCALAR_CMP_LE, + SCALAR_CMP_GE, + SCALAR_CMP_GT +}; + + +class Dec_ptr +{ +protected: + my_decimal *m_ptr; + Dec_ptr() { } +public: + bool is_null() const { return m_ptr == NULL; } + const my_decimal *ptr() const { return m_ptr; } + const my_decimal *ptr_or(const my_decimal *def) const + { + return m_ptr ? m_ptr : def; + } + my_decimal *to_decimal(my_decimal *to) const + { + if (!m_ptr) + return NULL; + *to= *m_ptr; + return to; + } + double to_double() const { return m_ptr ? m_ptr->to_double() : 0.0; } + longlong to_longlong(bool unsigned_flag) + { return m_ptr ? m_ptr->to_longlong(unsigned_flag) : 0; } + bool to_bool() const { return m_ptr ? m_ptr->to_bool() : false; } + bool to_datetime_with_warn(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate, + const char *field_name) + { + return m_ptr ? m_ptr->to_datetime_with_warn(thd, to, fuzzydate, field_name) : + true; + } + bool to_datetime_with_warn(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate, + Item *item); + String *to_string(String *to) const + { + return m_ptr ? m_ptr->to_string(to) : NULL; + } + String *to_string(String *to, uint prec, uint dec, char filler) + { + return m_ptr ? m_ptr->to_string(to, prec, dec, filler) : NULL; + } + int to_binary(uchar *bin, int prec, int scale) const + { + return (m_ptr ? m_ptr : &decimal_zero)->to_binary(bin, prec, scale); + } + int cmp(const my_decimal *dec) const + { + DBUG_ASSERT(m_ptr); + DBUG_ASSERT(dec); + return m_ptr->cmp(dec); + } + int cmp(const Dec_ptr &other) const + { + return cmp(other.m_ptr); + } +}; + + +// A helper class to handle results of val_decimal(), date_op(), etc. +class Dec_ptr_and_buffer: public Dec_ptr +{ +protected: + my_decimal m_buffer; +public: + int round_to(my_decimal *to, uint scale, decimal_round_mode mode) + { + DBUG_ASSERT(m_ptr); + return m_ptr->round_to(to, scale, mode); + } + int round_self(uint scale, decimal_round_mode mode) + { + return round_to(&m_buffer, scale, mode); + } + String *to_string_round(String *to, uint dec) + { + /* + decimal_round() allows from==to + So it's save even if m_ptr points to m_buffer before this call: + */ + return m_ptr ? m_ptr->to_string_round(to, dec, &m_buffer) : NULL; + } +}; + + +// A helper class to handle val_decimal() results. +class VDec: public Dec_ptr_and_buffer +{ +public: + VDec(): Dec_ptr_and_buffer() { } + VDec(Item *item); + void set(Item *a); +}; + + +// A helper class to handler decimal_op() results. +class VDec_op: public Dec_ptr_and_buffer +{ +public: + VDec_op(Item_func_hybrid_field_type *item); +}; + + +/* + Get and cache val_decimal() values for two items. + If the first value appears to be NULL, the second value is not evaluated. +*/ +class VDec2_lazy +{ +public: + VDec m_a; + VDec m_b; + VDec2_lazy(Item *a, Item *b) :m_a(a) + { + if (!m_a.is_null()) + m_b.set(b); + } + bool has_null() const + { + return m_a.is_null() || m_b.is_null(); + } +}; + + +/** + Class Sec6 represents a fixed point value with 6 fractional digits. + Used e.g. to convert double and my_decimal values to TIME/DATETIME. +*/ + +class Sec6 +{ +protected: + ulonglong m_sec; // The integer part, between 0 and LONGLONG_MAX + ulong m_usec; // The fractional part, between 0 and 999999 + bool m_neg; // false if positive, true of negative + bool m_truncated; // Indicates if the constructor truncated the value + void make_from_decimal(const my_decimal *d); + void make_from_double(double d); + void make_from_int(const Longlong_hybrid &nr) + { + m_neg= nr.neg(); + m_sec= nr.abs(); + m_usec= 0; + m_truncated= false; + } + void reset() + { + m_sec= m_usec= m_neg= m_truncated= 0; + } + Sec6() { } +public: + explicit Sec6(double nr) + { + make_from_double(nr); + } + explicit Sec6(const my_decimal *d) + { + make_from_decimal(d); + } + explicit Sec6(const Longlong_hybrid &nr) + { + make_from_int(nr); + } + explicit Sec6(longlong nr, bool unsigned_val) + { + make_from_int(Longlong_hybrid(nr, unsigned_val)); + } + bool neg() const { return m_neg; } + bool truncated() const { return m_truncated; } + ulonglong sec() const { return m_sec; } + long usec() const { return m_usec; } + /** + Converts Sec6 to MYSQL_TIME + + @param ltime converted value will be written here + @param fuzzydate conversion flags (TIME_INVALID_DATE, etc) + @param str original number, as an ErrConv. For the warning + @param field_name field name or NULL if not a field. For the warning + @returns false for success, true for a failure + */ + bool convert_to_mysql_time(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate, + const ErrConv *str, const char *field_name) const; + + // Convert a number in format hhhmmss.ff to TIME'hhh:mm:ss.ff' + bool to_time(MYSQL_TIME *to, int *warn) const + { + return number_to_time(m_neg, m_sec, m_usec, to, warn); + } + /* + Convert a number in format YYYYMMDDhhmmss.ff to + TIMESTAMP'YYYY-MM-DD hh:mm:ss.ff' + */ + bool to_datetime(MYSQL_TIME *to, date_mode_t flags, int *warn) const + { + if (m_neg) + { + *warn= MYSQL_TIME_WARN_OUT_OF_RANGE; + return true; + } + return number_to_datetime(m_sec, m_usec, to, + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), + warn) == -1; + } + // Convert elapsed seconds to TIME + bool sec_to_time(MYSQL_TIME *ltime, uint dec) const + { + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + ltime->neg= m_neg; + if (m_sec > TIME_MAX_VALUE_SECONDS) + { + // use check_time_range() to set ltime to the max value depending on dec + int unused; + ltime->hour= TIME_MAX_HOUR + 1; + check_time_range(ltime, dec, &unused); + return true; + } + DBUG_ASSERT(usec() <= TIME_MAX_SECOND_PART); + ltime->hour= (uint) (m_sec / 3600); + ltime->minute= (uint) (m_sec % 3600) / 60; + ltime->second= (uint) m_sec % 60; + ltime->second_part= m_usec; + return false; + } + size_t to_string(char *to, size_t nbytes) const + { + return m_usec ? + my_snprintf(to, nbytes, "%s%llu.%06lu", + m_neg ? "-" : "", m_sec, (uint) m_usec) : + my_snprintf(to, nbytes, "%s%llu", m_neg ? "-" : "", m_sec); + } + void make_truncated_warning(THD *thd, const char *type_str) const; +}; + + +class VSec6: public Sec6 +{ + bool m_is_null; +public: + VSec6(THD *thd, Item *item, const char *type_str, ulonglong limit); + bool is_null() const { return m_is_null; } +}; + + +/* + A heler class to perform additive operations between + two MYSQL_TIME structures and return the result as a + combination of seconds, microseconds and sign. +*/ +class Sec6_add +{ + ulonglong m_sec; // number of seconds + ulong m_usec; // number of microseconds + bool m_neg; // false if positive, true if negative + bool m_error; // false if the value is OK, true otherwise + void to_hh24mmssff(MYSQL_TIME *ltime, timestamp_type tstype) const + { + bzero(ltime, sizeof(*ltime)); + ltime->neg= m_neg; + calc_time_from_sec(ltime, (ulong) (m_sec % SECONDS_IN_24H), m_usec); + ltime->time_type= tstype; + } +public: + /* + @param ltime1 - the first value to add (must be a valid DATE,TIME,DATETIME) + @param ltime2 - the second value to add (must be a valid TIME) + @param sign - the sign of the operation + (+1 for addition, -1 for subtraction) + */ + Sec6_add(const MYSQL_TIME *ltime1, const MYSQL_TIME *ltime2, int sign) + { + DBUG_ASSERT(sign == -1 || sign == 1); + DBUG_ASSERT(!ltime1->neg || ltime1->time_type == MYSQL_TIMESTAMP_TIME); + if (!(m_error= (ltime2->time_type != MYSQL_TIMESTAMP_TIME))) + { + if (ltime1->neg != ltime2->neg) + sign= -sign; + m_neg= calc_time_diff(ltime1, ltime2, -sign, &m_sec, &m_usec); + if (ltime1->neg && (m_sec || m_usec)) + m_neg= !m_neg; // Swap sign + } + } + bool to_time(THD *thd, MYSQL_TIME *ltime, uint decimals) const + { + if (m_error) + return true; + to_hh24mmssff(ltime, MYSQL_TIMESTAMP_TIME); + ltime->hour+= to_days_abs() * 24; + return adjust_time_range_with_warn(thd, ltime, decimals); + } + bool to_datetime(MYSQL_TIME *ltime) const + { + if (m_error || m_neg) + return true; + to_hh24mmssff(ltime, MYSQL_TIMESTAMP_DATETIME); + return get_date_from_daynr(to_days_abs(), + <ime->year, <ime->month, <ime->day) || + !ltime->day; + } + long to_days_abs() const { return (long) (m_sec / SECONDS_IN_24H); } +}; + + +class Year +{ +protected: + uint m_year; + bool m_truncated; + bool to_mysql_time_with_warn(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate, + const char *field_name) const + { + // Make it YYYYMMDD + Longlong_hybrid value(static_cast<ulonglong>(m_year) * 10000, true); + const ErrConvInteger str(value); + Sec6 sec(value); + return sec.convert_to_mysql_time(thd, to, fuzzydate, &str, field_name); + } + uint year_precision(const Item *item) const; +public: + Year(): m_year(0), m_truncated(false) { } + Year(longlong value, bool unsigned_flag, uint length); + uint year() const { return m_year; } + bool truncated() const { return m_truncated; } +}; + + +class Year_null: public Year, public Null_flag +{ +public: + Year_null(const Longlong_null &nr, bool unsigned_flag, uint length) + :Year(nr.is_null() ? 0 : nr.value(), unsigned_flag, length), + Null_flag(nr.is_null()) + { } + bool to_mysql_time_with_warn(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate, + const char *field_name) const + { + return m_is_null ? true : + Year::to_mysql_time_with_warn(thd, to, fuzzydate, field_name); + } +}; + + +class VYear: public Year_null +{ +public: + VYear(Item *item); +}; + + +class VYear_op: public Year_null +{ +public: + VYear_op(Item_func_hybrid_field_type *item); +}; + + +class Temporal: protected MYSQL_TIME +{ +public: + static date_mode_t sql_mode_for_dates(THD *thd); + bool is_valid_temporal() const + { + DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR); + return time_type != MYSQL_TIMESTAMP_NONE; + } +protected: + my_decimal *bad_to_decimal(my_decimal *to) const; + my_decimal *to_decimal(my_decimal *to) const; + static double to_double(bool negate, ulonglong num, ulong frac) + { + double d= (double) num + frac / (double) TIME_SECOND_PART_FACTOR; + return negate ? -d : d; + } + longlong to_packed() const { return ::pack_time(this); } + void make_from_out_of_range(int *warn) + { + *warn= MYSQL_TIME_WARN_OUT_OF_RANGE; + time_type= MYSQL_TIMESTAMP_NONE; + } + bool str_to_time(MYSQL_TIME_STATUS *st, const char *str, size_t length, + CHARSET_INFO *cs, date_mode_t fuzzydate); + bool str_to_datetime(MYSQL_TIME_STATUS *st, const char *str, size_t length, + CHARSET_INFO *cs, date_mode_t fuzzydate); + bool has_valid_mmssff() const + { + return minute <= TIME_MAX_MINUTE && + second <= TIME_MAX_SECOND && + second_part <= TIME_MAX_SECOND_PART; + } + bool has_zero_YYYYMM() const + { + return year == 0 && month == 0; + } + bool has_zero_YYYYMMDD() const + { + return year == 0 && month == 0 && day == 0; + } +public: + static void *operator new(size_t size, MYSQL_TIME *ltime) throw() + { + DBUG_ASSERT(size == sizeof(MYSQL_TIME)); + return ltime; + } + static void operator delete(void *ptr, MYSQL_TIME *ltime) { } + + long fraction_remainder(uint dec) const + { + return my_time_fraction_remainder(second_part, dec); + } +}; + + +/* + Use this class when you need to get a MYSQL_TIME from an Item + using Item's native timestamp type, without automatic timestamp + type conversion. +*/ +class Temporal_hybrid: public Temporal +{ +public: + Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate); + Temporal_hybrid(THD *thd, Item *item) + :Temporal_hybrid(thd, item, sql_mode_for_dates(thd)) + { } + Temporal_hybrid(Item *item) + :Temporal_hybrid(current_thd, item) + { } + Temporal_hybrid(MYSQL_TIME_STATUS *st, const char *str, size_t length, + CHARSET_INFO *cs, date_mode_t fuzzydate) + { + if (str_to_datetime(st, str, length, cs, fuzzydate)) + time_type= MYSQL_TIMESTAMP_NONE; + } + Temporal_hybrid(THD *thd, const Sec6 &sec, date_mode_t fuzzydate, + const ErrConv *str, const char *field_name) + { + if (sec.convert_to_mysql_time(thd, this, fuzzydate, str, field_name)) + time_type= MYSQL_TIMESTAMP_NONE; + } + longlong to_longlong() const + { + if (!is_valid_temporal()) + return 0; + ulonglong v= TIME_to_ulonglong(this); + return neg ? -(longlong) v : (longlong) v; + } + double to_double() const + { + return is_valid_temporal() ? TIME_to_double(this) : 0; + } + my_decimal *to_decimal(my_decimal *to) + { + return is_valid_temporal() ? Temporal::to_decimal(to) : bad_to_decimal(to); + } + String *to_string(String *str, uint dec) const + { + if (!is_valid_temporal()) + return NULL; + str->set_charset(&my_charset_numeric); + if (!str->alloc(MAX_DATE_STRING_REP_LENGTH)) + str->length(my_TIME_to_str(this, const_cast<char*>(str->ptr()), dec)); + return str; + } + const MYSQL_TIME *get_mysql_time() const + { + DBUG_ASSERT(is_valid_temporal()); + return this; + } +}; + + +/* + This class resembles the SQL standard <extract source>, + used in extract expressions, e.g: EXTRACT(DAY FROM dt) + <extract expression> ::= + EXTRACT <left paren> <extract field> FROM <extract source> <right paren> + <extract source> ::= <datetime value expression> | <interval value expression> +*/ +class Extract_source: public Temporal_hybrid +{ + /* + Convert a TIME value to DAY-TIME interval, e.g. for extraction: + EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc. + Moves full days from ltime->hour to ltime->day. + */ + void time_to_daytime_interval() + { + DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_TIME); + DBUG_ASSERT(has_zero_YYYYMMDD()); + MYSQL_TIME::day= MYSQL_TIME::hour / 24; + MYSQL_TIME::hour%= 24; + } + bool is_valid_extract_source_slow() const + { + return is_valid_temporal() && MYSQL_TIME::hour < 24 && + (has_zero_YYYYMM() || time_type != MYSQL_TIMESTAMP_TIME); + } + bool is_valid_value_slow() const + { + return time_type == MYSQL_TIMESTAMP_NONE || is_valid_extract_source_slow(); + } +public: + Extract_source(THD *thd, Item *item, date_mode_t mode) + :Temporal_hybrid(thd, item, mode) + { + if (MYSQL_TIME::time_type == MYSQL_TIMESTAMP_TIME) + time_to_daytime_interval(); + DBUG_ASSERT(is_valid_value_slow()); + } + inline const MYSQL_TIME *get_mysql_time() const + { + DBUG_ASSERT(is_valid_extract_source_slow()); + return this; + } + bool is_valid_extract_source() const { return is_valid_temporal(); } + int sign() const { return get_mysql_time()->neg ? -1 : 1; } + uint year() const { return get_mysql_time()->year; } + uint month() const { return get_mysql_time()->month; } + int day() const { return (int) get_mysql_time()->day * sign(); } + int hour() const { return (int) get_mysql_time()->hour * sign(); } + int minute() const { return (int) get_mysql_time()->minute * sign(); } + int second() const { return (int) get_mysql_time()->second * sign(); } + int microsecond() const { return (int) get_mysql_time()->second_part * sign(); } + + uint year_month() const { return year() * 100 + month(); } + uint quarter() const { return (month() + 2)/3; } + uint week(THD *thd) const; + + longlong second_microsecond() const + { + return (second() * 1000000LL + microsecond()); + } + + // DAY TO XXX + longlong day_hour() const + { + return (longlong) day() * 100LL + hour(); + } + longlong day_minute() const + { + return day_hour() * 100LL + minute(); + } + longlong day_second() const + { + return day_minute() * 100LL + second(); + } + longlong day_microsecond() const + { + return day_second() * 1000000LL + microsecond(); + } + + // HOUR TO XXX + int hour_minute() const + { + return hour() * 100 + minute(); + } + int hour_second() const + { + return hour_minute() * 100 + second(); + } + longlong hour_microsecond() const + { + return hour_second() * 1000000LL + microsecond(); + } + + // MINUTE TO XXX + int minute_second() const + { + return minute() * 100 + second(); + } + longlong minute_microsecond() const + { + return minute_second() * 1000000LL + microsecond(); + } +}; + + +/* + This class is used for the "time_interval" argument of these SQL functions: + TIMESTAMP(tm,time_interval) + ADDTIME(tm,time_interval) + Features: + - DATE and DATETIME formats are treated as errors + - Preserves hours for TIME format as is, without limiting to TIME_MAX_HOUR +*/ +class Interval_DDhhmmssff: public Temporal +{ + static const LEX_CSTRING m_type_name; + bool str_to_DDhhmmssff(MYSQL_TIME_STATUS *status, + const char *str, size_t length, CHARSET_INFO *cs, + ulong max_hour); + void push_warning_wrong_or_truncated_value(THD *thd, + const ErrConv &str, + int warnings); + bool is_valid_interval_DDhhmmssff_slow() const + { + return time_type == MYSQL_TIMESTAMP_TIME && + has_zero_YYYYMMDD() && has_valid_mmssff(); + } + bool is_valid_value_slow() const + { + return time_type == MYSQL_TIMESTAMP_NONE || + is_valid_interval_DDhhmmssff_slow(); + } +public: + // Get fractional second precision from an Item + static uint fsp(THD *thd, Item *item); + /* + Maximum useful HOUR value: + TIMESTAMP'0001-01-01 00:00:00' + '87649415:59:59' = '9999-12-31 23:59:59' + This gives maximum possible interval values: + - '87649415:59:59.999999' (in 'hh:mm:ss.ff' format) + - '3652058 23:59:59.999999' (in 'DD hh:mm:ss.ff' format) + */ + static uint max_useful_hour() + { + return 87649415; + } +public: + Interval_DDhhmmssff(THD *thd, MYSQL_TIME_STATUS *st, bool push_warnings, + Item *item, ulong max_hour); + Interval_DDhhmmssff(THD *thd, Item *item) + { + MYSQL_TIME_STATUS st; + new(this) Interval_DDhhmmssff(thd, &st, true, item, max_useful_hour()); + } + const MYSQL_TIME *get_mysql_time() const + { + DBUG_ASSERT(is_valid_interval_DDhhmmssff_slow()); + return this; + } + bool is_valid_interval_DDhhmmssff() const + { + return time_type == MYSQL_TIMESTAMP_TIME; + } + bool is_valid_value() const + { + return time_type == MYSQL_TIMESTAMP_NONE || is_valid_interval_DDhhmmssff(); + } +}; + /** Class Time is designed to store valid TIME values. @@ -93,32 +746,35 @@ class Vers_history_point; Time derives from MYSQL_TIME privately to make sure it is accessed externally only in the valid state. */ -class Time: private MYSQL_TIME +class Time: public Temporal { public: enum datetime_to_time_mode_t { + DATETIME_TO_TIME_DISALLOW, DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS, - DATETIME_TO_TIME_YYYYMMDD_TRUNCATE + DATETIME_TO_TIME_YYYYMMDD_TRUNCATE, + DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY, + DATETIME_TO_TIME_MINUS_CURRENT_DATE }; class Options { - sql_mode_t m_get_date_flags; + date_mode_t m_get_date_flags; datetime_to_time_mode_t m_datetime_to_time_mode; public: Options() :m_get_date_flags(flags_for_get_date()), m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS) { } - Options(sql_mode_t flags) + Options(date_mode_t flags) :m_get_date_flags(flags), m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS) { } - Options(sql_mode_t flags, datetime_to_time_mode_t dtmode) + Options(date_mode_t flags, datetime_to_time_mode_t dtmode) :m_get_date_flags(flags), m_datetime_to_time_mode(dtmode) { } - sql_mode_t get_date_flags() const + date_mode_t get_date_flags() const { return m_get_date_flags; } datetime_to_time_mode_t datetime_to_time_mode() const { return m_datetime_to_time_mode; } @@ -143,46 +799,79 @@ private: bool is_valid_time_slow() const { return time_type == MYSQL_TIMESTAMP_TIME && - year == 0 && month == 0 && day == 0 && - minute <= TIME_MAX_MINUTE && - second <= TIME_MAX_SECOND && - second_part <= TIME_MAX_SECOND_PART; + has_zero_YYYYMMDD() && has_valid_mmssff(); + } + void hhmmssff_copy(const MYSQL_TIME *from) + { + hour= from->hour; + minute= from->minute; + second= from->second; + second_part= from->second_part; + } + void datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(int *warn, + uint from_year, + uint from_month, + uint from_day) + { + if (from_year != 0 || from_month != 0) + *warn|= MYSQL_TIME_NOTE_TRUNCATED; + else + hour+= from_day * 24; + } + /* + The result is calculated effectively similar to: + TIMEDIFF(dt, CAST(CURRENT_DATE AS DATETIME)) + If the difference does not fit to the supported TIME range, it's truncated. + */ + void datetime_to_time_minus_current_date(THD *thd) + { + MYSQL_TIME current_date, tmp; + set_current_date(thd, ¤t_date); + calc_time_diff(this, ¤t_date, 1, &tmp, date_mode_t(0)); + static_cast<MYSQL_TIME*>(this)[0]= tmp; + int warnings= 0; + (void) check_time_range(this, TIME_SECOND_PART_DIGITS, &warnings); + DBUG_ASSERT(is_valid_time()); } - /* Convert a valid DATE or DATETIME to TIME. Before this call, "this" must be a valid DATE or DATETIME value, - e.g. returned from Item::get_date(). + e.g. returned from Item::get_date(), str_to_time(), number_to_time(). After this call, "this" is a valid TIME value. */ - void valid_datetime_to_valid_time(const Options opt) + void valid_datetime_to_valid_time(THD *thd, int *warn, const Options opt) { DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATE || time_type == MYSQL_TIMESTAMP_DATETIME); /* - Make sure that day and hour are valid, so the result hour value + We're dealing with a DATE or DATETIME returned from + str_to_time(), number_to_time() or unpack_time(). + Do some asserts to make sure the result hour value after mixing days to hours does not go out of the valid TIME range. + The maximum hour value after mixing days will be 31*24+23=767, + which is within the supported TIME range. + Thus no adjust_time_range_or_invalidate() is needed here. */ DBUG_ASSERT(day < 32); DBUG_ASSERT(hour < 24); - if (year == 0 && month == 0 && - opt.datetime_to_time_mode() == - DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS) + if (opt.datetime_to_time_mode() == DATETIME_TO_TIME_MINUS_CURRENT_DATE) { - /* - The maximum hour value after mixing days will be 31*24+23=767, - which is within the supported TIME range. - Thus no adjust_time_range_or_invalidate() is needed here. - */ - hour+= day * 24; + datetime_to_time_minus_current_date(thd); + } + else + { + if (opt.datetime_to_time_mode() == + DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS) + datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(warn, year, month, day); + year= month= day= 0; + time_type= MYSQL_TIMESTAMP_TIME; } - year= month= day= 0; - time_type= MYSQL_TIMESTAMP_TIME; DBUG_ASSERT(is_valid_time_slow()); } /** Convert valid DATE/DATETIME to valid TIME if needed. This method is called after Item::get_date(), + str_to_time(), number_to_time(). which can return only valid TIME/DATE/DATETIME values. Before this call, "this" is: - either a valid TIME/DATE/DATETIME value @@ -192,12 +881,19 @@ private: - either a valid TIME (within the supported TIME range), - or MYSQL_TIMESTAMP_NONE */ - void valid_MYSQL_TIME_to_valid_value(const Options opt) + void valid_MYSQL_TIME_to_valid_value(THD *thd, int *warn, const Options opt) { switch (time_type) { case MYSQL_TIMESTAMP_DATE: case MYSQL_TIMESTAMP_DATETIME: - valid_datetime_to_valid_time(opt); + if (opt.datetime_to_time_mode() == + DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY && + (year || month || day)) + make_from_out_of_range(warn); + else if (opt.datetime_to_time_mode() == DATETIME_TO_TIME_DISALLOW) + make_from_out_of_range(warn); + else + valid_datetime_to_valid_time(thd, warn, opt); break; case MYSQL_TIMESTAMP_NONE: break; @@ -209,14 +905,94 @@ private: break; } } - void make_from_item(class Item *item, const Options opt); + + /* + This method is called after number_to_time() and str_to_time(), + which can return DATE or DATETIME values. Convert to TIME if needed. + We trust that xxx_to_time() returns a valid TIME/DATE/DATETIME value, + so here we need to do only simple validation. + */ + void xxx_to_time_result_to_valid_value(THD *thd, int *warn, const Options opt) + { + // str_to_time(), number_to_time() never return MYSQL_TIMESTAMP_ERROR + DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR); + valid_MYSQL_TIME_to_valid_value(thd, warn, opt); + } + void adjust_time_range_or_invalidate(int *warn) + { + if (check_time_range(this, TIME_SECOND_PART_DIGITS, warn)) + time_type= MYSQL_TIMESTAMP_NONE; + DBUG_ASSERT(is_valid_value_slow()); + } + void make_from_datetime_move_day_to_hour(int *warn, const MYSQL_TIME *from); + void make_from_datetime_with_days_diff(int *warn, const MYSQL_TIME *from, + long curdays); + void make_from_time(int *warn, const MYSQL_TIME *from); + void make_from_datetime(int *warn, const MYSQL_TIME *from, long curdays); + void make_from_item(THD *thd, int *warn, Item *item, const Options opt); public: + /* + All constructors that accept an "int *warn" parameter initialize *warn. + The old value gets lost. + */ Time() { time_type= MYSQL_TIMESTAMP_NONE; } - Time(Item *item) { make_from_item(item, Options()); } - Time(Item *item, const Options opt) { make_from_item(item, opt); } - static sql_mode_t flags_for_get_date() + Time(Item *item) + :Time(current_thd, item, Options()) + { } + Time(THD *thd, Item *item, const Options opt) + { + int warn; + make_from_item(thd, &warn, item, opt); + } + Time(THD *thd, Item *item) + :Time(thd, item, Options()) + { } + Time(int *warn, const MYSQL_TIME *from, long curdays); + Time(THD *thd, MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, + const Options opt) + { + if (str_to_time(status, str, len, cs, opt.get_date_flags())) + time_type= MYSQL_TIMESTAMP_NONE; + // The below call will optionally add notes to already collected warnings: + xxx_to_time_result_to_valid_value(thd, &status->warnings, opt); + } + Time(THD *thd, int *warn, const Sec6 &nr, const Options opt) + { + if (nr.to_time(this, warn)) + time_type= MYSQL_TIMESTAMP_NONE; + xxx_to_time_result_to_valid_value(thd, warn, opt); + } + Time(THD *thd, int *warn, const Sec6 &nr) + :Time(thd, warn, nr, Options()) + { } + + Time(THD *thd, Item *item, const Options opt, uint dec) + :Time(thd, item, opt) + { + trunc(dec); + } + Time(int *warn, const MYSQL_TIME *from, long curdays, uint dec) + :Time(warn, from, curdays) + { + trunc(dec); + } + Time(THD *thd, MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, + const Options &opt, uint dec) + :Time(thd, status, str, len, cs, opt) + { + trunc(dec); + } + Time(THD *thd, int *warn, const Sec6 &nr, uint dec) + :Time(thd, warn, nr) + { + trunc(dec); + } + + static date_mode_t flags_for_get_date() { return TIME_TIME_ONLY | TIME_INVALID_DATES; } - static sql_mode_t comparison_flags_for_get_date() + static date_mode_t comparison_flags_for_get_date() { return TIME_TIME_ONLY | TIME_INVALID_DATES | TIME_FUZZY_DATES; } bool is_valid_time() const { @@ -243,8 +1019,8 @@ public: { DBUG_ASSERT(is_valid_time_slow()); DBUG_ASSERT(other->is_valid_time_slow()); - longlong p0= pack_time(this); - longlong p1= pack_time(other); + longlong p0= to_packed(); + longlong p1= other->to_packed(); if (p0 < p1) return -1; if (p0 > p1) @@ -260,6 +1036,42 @@ public: { return neg ? -to_seconds_abs() : to_seconds_abs(); } + longlong to_longlong() const + { + if (!is_valid_time()) + return 0; + ulonglong v= TIME_to_ulonglong_time(this); + return neg ? -(longlong) v : (longlong) v; + } + double to_double() const + { + return !is_valid_time() ? 0 : + Temporal::to_double(neg, TIME_to_ulonglong_time(this), second_part); + } + String *to_string(String *str, uint dec) const + { + if (!is_valid_time()) + return NULL; + str->set_charset(&my_charset_numeric); + if (!str->alloc(MAX_DATE_STRING_REP_LENGTH)) + str->length(my_time_to_str(this, const_cast<char*>(str->ptr()), dec)); + return str; + } + my_decimal *to_decimal(my_decimal *to) + { + return is_valid_time() ? Temporal::to_decimal(to) : bad_to_decimal(to); + } + longlong to_packed() const + { + return is_valid_time() ? Temporal::to_packed() : 0; + } + Time &trunc(uint dec) + { + if (is_valid_time()) + my_time_trunc(this, dec); + DBUG_ASSERT(is_valid_value_slow()); + return *this; + } }; @@ -286,14 +1098,70 @@ public: it is accessed externally only in the valid state. */ -class Temporal_with_date: protected MYSQL_TIME +class Temporal_with_date: public Temporal { protected: - void make_from_item(THD *thd, Item *item, sql_mode_t flags); - Temporal_with_date(THD *thd, Item *item, sql_mode_t flags) + void check_date_or_invalidate(int *warn, date_mode_t flags); + void make_from_item(THD *thd, Item *item, date_mode_t flags); + void make_from_item(THD *thd, Item *item); + + ulong daynr() const + { + return (ulong) ::calc_daynr((uint) year, (uint) month, (uint) day); + } + ulong dayofyear() const + { + return (ulong) (daynr() - ::calc_daynr(year, 1, 1) + 1); + } + uint quarter() const + { + return (month + 2) / 3; + } + uint week(uint week_behaviour) const + { + uint year; + return calc_week(this, week_behaviour, &year); + } + uint yearweek(uint week_behaviour) const + { + uint year; + uint week= calc_week(this, week_behaviour, &year); + return week + year * 100; + } + + Temporal_with_date() + { + time_type= MYSQL_TIMESTAMP_NONE; + } + Temporal_with_date(THD *thd, Item *item, date_mode_t flags) { make_from_item(thd, item, flags); } + Temporal_with_date(THD *thd, Item *item) + { + make_from_item(thd, item); + } + Temporal_with_date(int *warn, const Sec6 &nr, date_mode_t flags) + { + DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false); + if (nr.to_datetime(this, flags, warn)) + time_type= MYSQL_TIMESTAMP_NONE; + } + Temporal_with_date(MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, + date_mode_t flags) + { + DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false); + if (str_to_datetime(status, str, len, cs, flags)) + time_type= MYSQL_TIMESTAMP_NONE; + } +public: + bool check_date_with_warn(THD *thd, date_mode_t flags) + { + return ::check_date_with_warn(thd, this, flags, MYSQL_TIMESTAMP_ERROR); + } + static date_mode_t comparison_flags_for_get_date() + { return TIME_INVALID_DATES | TIME_FUZZY_DATES; } }; @@ -318,13 +1186,23 @@ class Date: public Temporal_with_date return !check_datetime_range(this); } public: - Date(THD *thd, Item *item, sql_mode_t flags) + Date(THD *thd, Item *item, date_mode_t flags) :Temporal_with_date(thd, item, flags) { if (time_type == MYSQL_TIMESTAMP_DATETIME) datetime_to_date(this); DBUG_ASSERT(is_valid_value_slow()); } + Date(THD *thd, Item *item) + :Temporal_with_date(thd, item) + { + if (time_type == MYSQL_TIMESTAMP_DATETIME) + datetime_to_date(this); + DBUG_ASSERT(is_valid_value_slow()); + } + Date(Item *item) + :Date(current_thd, item) + { } Date(const Temporal_with_date *d) :Temporal_with_date(*d) { @@ -341,6 +1219,64 @@ public: DBUG_ASSERT(is_valid_date_slow()); return this; } + bool copy_to_mysql_time(MYSQL_TIME *ltime) const + { + if (time_type == MYSQL_TIMESTAMP_NONE) + { + ltime->time_type= MYSQL_TIMESTAMP_NONE; + return true; + } + DBUG_ASSERT(is_valid_date_slow()); + *ltime= *this; + return false; + } + ulong daynr() const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::daynr(); + } + ulong dayofyear() const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::dayofyear(); + } + uint quarter() const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::quarter(); + } + uint week(uint week_behaviour) const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::week(week_behaviour); + } + uint yearweek(uint week_behaviour) const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::yearweek(week_behaviour); + } + + longlong to_longlong() const + { + return is_valid_date() ? (longlong) TIME_to_ulonglong_date(this) : 0LL; + } + double to_double() const + { + return (double) to_longlong(); + } + String *to_string(String *str) const + { + if (!is_valid_date()) + return NULL; + str->set_charset(&my_charset_numeric); + if (!str->alloc(MAX_DATE_STRING_REP_LENGTH)) + str->length(my_date_to_str(this, const_cast<char*>(str->ptr()))); + return str; + } + my_decimal *to_decimal(my_decimal *to) + { + return is_valid_date() ? Temporal::to_decimal(to) : bad_to_decimal(to); + } }; @@ -365,14 +1301,75 @@ class Datetime: public Temporal_with_date DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATETIME); return !check_datetime_range(this); } -public: - Datetime(THD *thd, Item *item, sql_mode_t flags) - :Temporal_with_date(thd, item, flags) + void date_to_datetime_if_needed() { if (time_type == MYSQL_TIMESTAMP_DATE) date_to_datetime(this); + } + void make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, + date_mode_t flags); + void make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from, + date_mode_t flags); +public: + Datetime(THD *thd, Item *item, date_mode_t flags) + :Temporal_with_date(thd, item, flags) + { + date_to_datetime_if_needed(); + DBUG_ASSERT(is_valid_value_slow()); + } + Datetime(THD *thd, Item *item) + :Temporal_with_date(thd, item) + { + date_to_datetime_if_needed(); DBUG_ASSERT(is_valid_value_slow()); } + Datetime(Item *item) + :Datetime(current_thd, item) + { } + Datetime(THD *thd, int *warn, const MYSQL_TIME *from, date_mode_t flags); + Datetime() + { + set_zero_time(this, MYSQL_TIMESTAMP_DATETIME); + } + Datetime(MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, + date_mode_t flags) + :Temporal_with_date(status, str, len, cs, flags) + { + date_to_datetime_if_needed(); + DBUG_ASSERT(is_valid_value_slow()); + } + Datetime(int *warn, const Sec6 &nr, date_mode_t flags) + :Temporal_with_date(warn, nr, flags) + { + date_to_datetime_if_needed(); + DBUG_ASSERT(is_valid_value_slow()); + } + + Datetime(THD *thd, Item *item, date_mode_t flags, uint dec) + :Datetime(thd, item, flags) + { + trunc(dec); + } + Datetime(MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, + date_mode_t fuzzydate, uint dec) + :Datetime(status, str, len, cs, fuzzydate) + { + trunc(dec); + } + Datetime(int *warn, const Sec6 &nr, date_mode_t fuzzydate, uint dec) + :Datetime(warn, nr, fuzzydate) + { + trunc(dec); + } + Datetime(THD *thd, int *warn, const MYSQL_TIME *from, + date_mode_t fuzzydate, uint dec) + :Datetime(thd, warn, from, fuzzydate) + { + trunc(dec); + } + bool is_valid_datetime() const { /* @@ -382,11 +1379,42 @@ public: DBUG_ASSERT(is_valid_value_slow()); return time_type == MYSQL_TIMESTAMP_DATETIME; } + bool check_date(date_mode_t flags, int *warnings) const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return ::check_date(this, (year || month || day), + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), + warnings); + } + bool check_date(date_mode_t flags) const + { + int dummy; /* unused */ + return check_date(flags, &dummy); + } bool hhmmssff_is_zero() const { DBUG_ASSERT(is_valid_datetime_slow()); return hour == 0 && minute == 0 && second == 0 && second_part == 0; } + ulong daynr() const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return Temporal_with_date::daynr(); + } + longlong hhmmss_to_seconds_abs() const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return hour * 3600L + minute * 60 + second; + } + longlong hhmmss_to_seconds() const + { + return neg ? -hhmmss_to_seconds_abs() : hhmmss_to_seconds_abs(); + } + longlong to_seconds() const + { + return hhmmss_to_seconds() + (longlong) daynr() * 24L * 3600L; + } + const MYSQL_TIME *get_mysql_time() const { DBUG_ASSERT(is_valid_datetime_slow()); @@ -418,8 +1446,43 @@ public: ltime->time_type= type; return false; } + longlong to_longlong() const + { + return is_valid_datetime() ? + (longlong) TIME_to_ulonglong_datetime(this) : 0LL; + } + double to_double() const + { + return !is_valid_datetime() ? 0 : + Temporal::to_double(neg, TIME_to_ulonglong_datetime(this), second_part); + } + String *to_string(String *str, uint dec) const + { + if (!is_valid_datetime()) + return NULL; + str->set_charset(&my_charset_numeric); + if (!str->alloc(MAX_DATE_STRING_REP_LENGTH)) + str->length(my_datetime_to_str(this, const_cast<char*>(str->ptr()), dec)); + return str; + } + my_decimal *to_decimal(my_decimal *to) + { + return is_valid_datetime() ? Temporal::to_decimal(to) : bad_to_decimal(to); + } + longlong to_packed() const + { + return is_valid_datetime() ? Temporal::to_packed() : 0; + } + Datetime &trunc(uint dec) + { + if (is_valid_datetime()) + my_time_trunc(this, dec); + DBUG_ASSERT(is_valid_value_slow()); + return *this; + } }; + /* Flags for collation aggregation modes, used in TDCollation::agg(): @@ -445,7 +1508,6 @@ public: #define MY_COLL_CMP_CONV (MY_COLL_ALLOW_CONV | MY_COLL_DISALLOW_NONE) -#define my_charset_numeric my_charset_latin1 #define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII @@ -834,6 +1896,14 @@ public: }; +class Type_cmp_attributes +{ +public: + virtual ~Type_cmp_attributes() { } + virtual CHARSET_INFO *compare_collation() const= 0; +}; + + class Type_cast_attributes { CHARSET_INFO *m_charset; @@ -886,28 +1956,67 @@ public: }; -class Record_addr +class Bit_addr { -public: - uchar *ptr; // Position to field in record /** - Byte where the @c NULL bit is stored inside a record. If this Field is a - @c NOT @c NULL field, this member is @c NULL. + Byte where the bit is stored inside a record. + If the corresponding Field is a NOT NULL field, this member is NULL. + */ + uchar *m_ptr; + /** + Offset of the bit inside m_ptr[0], in the range 0..7. */ - uchar *null_ptr; - uchar null_bit; // Bit used to test null bit + uchar m_offs; +public: + Bit_addr() + :m_ptr(NULL), + m_offs(0) + { } + Bit_addr(uchar *ptr, uchar offs) + :m_ptr(ptr), m_offs(offs) + { + DBUG_ASSERT(ptr || offs == 0); + DBUG_ASSERT(offs < 8); + } + Bit_addr(bool maybe_null) + :m_ptr(maybe_null ? (uchar *) "" : NULL), + m_offs(0) + { } + uchar *ptr() const { return m_ptr; } + uchar offs() const { return m_offs; } + uchar bit() const { return m_ptr ? ((uchar) 1) << m_offs : 0; } + void inc() + { + DBUG_ASSERT(m_ptr); + m_ptr+= (m_offs == 7); + m_offs= (m_offs + 1) & 7; + } +}; + + +class Record_addr +{ + uchar *m_ptr; // Position of the field in the record + Bit_addr m_null; // Position and offset of the null bit +public: Record_addr(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg) - :ptr(ptr_arg), - null_ptr(null_ptr_arg), - null_bit(null_bit_arg) + :m_ptr(ptr_arg), + m_null(null_ptr_arg, null_bit_arg) + { } + Record_addr(uchar *ptr, const Bit_addr &null) + :m_ptr(ptr), + m_null(null) { } Record_addr(bool maybe_null) - :ptr(NULL), - null_ptr(maybe_null ? (uchar*) "" : 0), - null_bit(0) + :m_ptr(NULL), + m_null(maybe_null) { } + uchar *ptr() const { return m_ptr; } + const Bit_addr &null() const { return m_null; } + uchar *null_ptr() const { return m_null.ptr(); } + uchar null_bit() const { return m_null.bit(); } }; @@ -982,6 +2091,9 @@ public: class Type_handler { protected: + static const Name m_version_default; + static const Name m_version_mysql56; + static const Name m_version_mariadb53; String *print_item_value_csstr(THD *thd, Item *item, String *str) const; String *print_item_value_temporal(THD *thd, Item *item, String *str, const Name &type_name, String *buf) const; @@ -1014,6 +2126,7 @@ protected: enum_field_types type) const; public: + static const Type_handler *odbc_literal_type_handler(const LEX_CSTRING *str); static const Type_handler *blob_type_handler(uint max_octet_length); static const Type_handler *string_type_handler(uint max_octet_length); static const Type_handler *bit_and_int_mixture_handler(uint max_char_len); @@ -1047,8 +2160,31 @@ public: const Type_handler *h2); virtual const Name name() const= 0; + virtual const Name version() const { return m_version_default; } virtual enum_field_types field_type() const= 0; virtual enum_field_types real_field_type() const { return field_type(); } + /** + Type code which is used for merging of traditional data types for result + (for UNION and for hybrid functions such as COALESCE). + Mapping can be done both ways: old->new, new->old, depending + on the particular data type implementation: + - type_handler_var_string (MySQL-4.1 old VARCHAR) is converted to + new VARCHAR before merging. + field_type_merge_rules[][] returns new VARCHAR. + - type_handler_newdate is converted to old DATE before merging. + field_type_merge_rules[][] returns NEWDATE. + - Temporal type_handler_xxx2 (new MySQL-5.6 types) are converted to + corresponding old type codes before merging (e.g. TIME2->TIME). + field_type_merge_rules[][] returns old type codes (e.g. TIME). + Then old types codes are supposed to convert to new type codes somehow, + but they do not. So UNION and COALESCE create old columns. + This is a bug and should be fixed eventually. + */ + virtual enum_field_types traditional_merge_field_type() const + { + DBUG_ASSERT(is_traditional_type()); + return field_type(); + } virtual Item_result result_type() const= 0; virtual Item_result cmp_type() const= 0; virtual enum_mysql_timestamp_type mysql_timestamp_type() const @@ -1059,6 +2195,25 @@ public: { return false; } + virtual bool is_order_clause_position_type() const + { + return false; + } + virtual bool is_limit_clause_valid_type() const + { + return false; + } + /* + Returns true if this data type supports a hack that + WHERE notnull_column IS NULL + finds zero values, e.g.: + WHERE date_notnull_column IS NULL -> + WHERE date_notnull_column = '0000-00-00' + */ + virtual bool cond_notnull_field_isnull_to_field_eq_zero() const + { + return false; + } /** Check whether a field type can be partially indexed by a key. @param type field type @@ -1097,6 +2252,12 @@ public: { return this; } + virtual const Type_handler *type_handler_for_system_time() const + { + return this; + } + virtual int + stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const= 0; virtual CHARSET_INFO *charset_for_protocol(const Item *item) const; virtual const Type_handler* type_handler_adjusted_to_max_octet_length(uint max_octet_length, @@ -1123,9 +2284,10 @@ public: virtual bool can_return_text() const { return true; } virtual bool can_return_date() const { return true; } virtual bool can_return_time() const { return true; } + virtual bool is_bool_type() const { return false; } virtual bool is_general_purpose_string_type() const { return false; } - virtual uint Item_time_precision(Item *item) const; - virtual uint Item_datetime_precision(Item *item) const; + virtual uint Item_time_precision(THD *thd, Item *item) const; + virtual uint Item_datetime_precision(THD *thd, Item *item) const; virtual uint Item_decimal_scale(const Item *item) const; virtual uint Item_decimal_precision(const Item *item) const= 0; /* @@ -1169,7 +2331,20 @@ public: virtual Field *make_conversion_table_field(TABLE *TABLE, uint metadata, const Field *target) const= 0; + // Automatic upgrade, e.g. for ALTER TABLE t1 FORCE + virtual void Column_definition_implicit_upgrade(Column_definition *c) const + { } + // Fix attributes after the parser virtual bool Column_definition_fix_attributes(Column_definition *c) const= 0; + /* + Fix attributes from an existing field. Used for: + - ALTER TABLE (for columns that do not change) + - DECLARE var TYPE OF t1.col1; (anchored SP variables) + */ + virtual void Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *c, + const Field *field) const + { } virtual bool Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *c, @@ -1208,6 +2383,23 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + virtual Field * + make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const= 0; + virtual void + Column_definition_attributes_frm_pack(const Column_definition_attributes *at, + uchar *buff) const; + virtual bool + Column_definition_attributes_frm_unpack(Column_definition_attributes *attr, + TABLE_SHARE *share, + const uchar *buffer, + LEX_CUSTRING *gis_options) const; + virtual void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, Sort_param *param) const= 0; @@ -1217,7 +2409,8 @@ public: virtual uint32 max_display_length(const Item *item) const= 0; virtual uint32 calc_pack_length(uint32 length) const= 0; - virtual bool Item_save_in_value(Item *item, st_value *value) const= 0; + virtual void Item_update_null_value(Item *item) const= 0; + virtual bool Item_save_in_value(THD *thd, Item *item, st_value *value) const= 0; virtual void Item_param_setup_conversion(THD *thd, Item_param *) const {} virtual void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; @@ -1295,6 +2488,32 @@ public: Item *src, const Item *cmp) const= 0; virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0; + /** + A builder for literals with data type name prefix, e.g.: + TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'. + @param thd The current thread + @param str Character literal + @param length Length of str + @param cs Character set of the string + @param send_error Whether to generate an error on failure + + @retval A pointer to a new Item on success + NULL on error (wrong literal value, EOM) + */ + virtual Item_literal *create_literal_item(THD *thd, + const char *str, size_t length, + CHARSET_INFO *cs, + bool send_error) const + { + DBUG_ASSERT(0); + return NULL; + } + Item_literal *create_literal_item(THD *thd, const String *str, + bool send_error) const + { + return create_literal_item(thd, str->ptr(), str->length(), str->charset(), + send_error); + } virtual Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const { @@ -1302,6 +2521,13 @@ public: return NULL; } virtual bool set_comparator_func(Arg_comparator *cmp) const= 0; + virtual bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const + { + return false; + } + virtual bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const= 0; virtual bool Item_hybrid_func_fix_attributes(THD *thd, const char *name, Type_handler_hybrid_field_type *, @@ -1319,8 +2545,8 @@ public: bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const= 0; virtual bool Item_val_bool(Item *item) const= 0; - virtual bool Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const= 0; + virtual bool Item_get_date(THD *thd, Item *item, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const= 0; virtual longlong Item_val_int_signed_typecast(Item *item) const= 0; virtual longlong Item_val_int_unsigned_typecast(Item *item) const= 0; @@ -1341,9 +2567,10 @@ public: Item_func_hybrid_field_type *, my_decimal *) const= 0; virtual - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + bool Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, MYSQL_TIME *, - ulonglong fuzzydate) const= 0; + date_mode_t fuzzydate) const= 0; virtual String *Item_func_min_max_val_str(Item_func_min_max *, String *) const= 0; virtual @@ -1354,8 +2581,8 @@ public: my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *) const= 0; virtual - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const= 0; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const= 0; virtual bool Item_func_between_fix_length_and_dec(Item_func_between *func) const= 0; virtual longlong @@ -1447,6 +2674,11 @@ public: return ROW_RESULT; } const Type_handler *type_handler_for_comparison() const; + int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const + { + DBUG_ASSERT(0); + return 0; + } bool subquery_type_allows_materialization(const Item *inner, const Item *outer) const { @@ -1469,6 +2701,12 @@ public: { return false; } + void Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *c, + const Field *field) const + { + DBUG_ASSERT(0); + } bool Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *c, @@ -1497,6 +2735,13 @@ public: DBUG_ASSERT(0); return NULL; } + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, Sort_param *param) const @@ -1518,12 +2763,14 @@ public: DBUG_ASSERT(0); return 0; } + bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const; uint Item_decimal_precision(const Item *item) const { DBUG_ASSERT(0); return DECIMAL_MAX_PRECISION; } - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_param_set_from_value(THD *thd, Item_param *param, const Type_all_attributes *attr, @@ -1533,6 +2780,7 @@ public: DBUG_ASSERT(0); return true; } + void Item_update_null_value(Item *item) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const { DBUG_ASSERT(0); @@ -1584,7 +2832,8 @@ public: DBUG_ASSERT(0); return false; } - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const + bool Item_get_date(THD *thd, Item *item, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const { DBUG_ASSERT(0); return true; @@ -1629,9 +2878,10 @@ public: DBUG_ASSERT(0); return NULL; } - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + bool Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, MYSQL_TIME *, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { DBUG_ASSERT(0); return true; @@ -1658,8 +2908,8 @@ public: DBUG_ASSERT(0); return NULL; } - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const { DBUG_ASSERT(0); return true; @@ -1743,8 +2993,8 @@ public: longlong Item_func_min_max_val_int(Item_func_min_max *) const; my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *) const; - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const; virtual ~Type_handler_numeric() { } bool can_change_cond_ref_to_const(Item_bool_func2 *target, Item *target_expr, Item *target_value, @@ -1764,6 +3014,10 @@ public: Item_result cmp_type() const { return REAL_RESULT; } virtual ~Type_handler_real_result() {} const Type_handler *type_handler_for_comparison() const; + void Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *c, + const Field *field) const; + int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const; bool subquery_type_allows_materialization(const Item *inner, const Item *outer) const; void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, @@ -1771,12 +3025,17 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const; + bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const; uint Item_decimal_precision(const Item *item) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_param_set_from_value(THD *thd, Item_param *param, const Type_all_attributes *attr, const st_value *value) const; + void Item_update_null_value(Item *item) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; @@ -1795,7 +3054,8 @@ public: bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const; bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const; bool Item_val_bool(Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + bool Item_get_date(THD *thd, Item *item, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const; longlong Item_val_int_signed_typecast(Item *item) const; longlong Item_val_int_unsigned_typecast(Item *item) const; String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; @@ -1808,9 +3068,10 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + bool Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, MYSQL_TIME *, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; longlong Item_func_between_val_int(Item_func_between *func) const; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; @@ -1837,6 +3098,11 @@ public: Item_result cmp_type() const { return DECIMAL_RESULT; } virtual ~Type_handler_decimal_result() {}; const Type_handler *type_handler_for_comparison() const; + int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const + { + VDec item_val(item); + return item_val.is_null() ? 0 : my_decimal(field).cmp(item_val.ptr()); + } bool subquery_type_allows_materialization(const Item *inner, const Item *outer) const; Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const; @@ -1848,8 +3114,16 @@ public: uint32 max_display_length(const Item *item) const; Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const; + bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const; + bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const + { + VDec va(a), vb(b); + return va.ptr() && vb.ptr() && !va.cmp(vb); + } uint Item_decimal_precision(const Item *item) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; bool Item_param_set_from_value(THD *thd, @@ -1860,6 +3134,7 @@ public: { return Item_send_str(item, protocol, buf); } + void Item_update_null_value(Item *item) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; @@ -1873,10 +3148,20 @@ public: bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; - bool Item_val_bool(Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + bool Item_val_bool(Item *item) const + { + return VDec(item).to_bool(); + } + bool Item_get_date(THD *thd, Item *item, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const + { + return VDec(item).to_datetime_with_warn(thd, ltime, fuzzydate, item); + } longlong Item_val_int_signed_typecast(Item *item) const; - longlong Item_val_int_unsigned_typecast(Item *item) const; + longlong Item_val_int_unsigned_typecast(Item *item) const + { + return VDec(item).to_longlong(true); + } String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *, String *) const; @@ -1887,9 +3172,10 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + bool Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, MYSQL_TIME *, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; longlong Item_func_between_val_int(Item_func_between *func) const; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; @@ -2038,8 +3324,11 @@ class Type_handler_int_result: public Type_handler_numeric public: Item_result result_type() const { return INT_RESULT; } Item_result cmp_type() const { return INT_RESULT; } + bool is_order_clause_position_type() const { return true; } + bool is_limit_clause_valid_type() const { return true; } virtual ~Type_handler_int_result() {} const Type_handler *type_handler_for_comparison() const; + int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const; bool subquery_type_allows_materialization(const Item *inner, const Item *outer) const; Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const; @@ -2048,12 +3337,17 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const; + bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const; uint Item_decimal_precision(const Item *item) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_param_set_from_value(THD *thd, Item_param *param, const Type_all_attributes *attr, const st_value *value) const; + void Item_update_null_value(Item *item) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; @@ -2068,7 +3362,8 @@ public: bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; bool Item_val_bool(Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + bool Item_get_date(THD *thd, Item *item, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const; longlong Item_val_int_signed_typecast(Item *item) const; longlong Item_val_int_unsigned_typecast(Item *item) const; String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; @@ -2081,9 +3376,10 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + bool Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, MYSQL_TIME *, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; longlong Item_func_between_val_int(Item_func_between *func) const; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; @@ -2099,6 +3395,7 @@ public: bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; bool Item_func_div_fix_length_and_dec(Item_func_div *) const; bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; + }; @@ -2127,6 +3424,8 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const; bool Item_param_set_from_value(THD *thd, Item_param *param, const Type_all_attributes *attr, @@ -2138,12 +3437,15 @@ public: Item *source_expr, Item *source_const) const; bool subquery_type_allows_materialization(const Item *inner, const Item *outer) const; + bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const; bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; bool Item_val_bool(Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + bool Item_get_date(THD *thd, Item *item, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const; longlong Item_val_int_signed_typecast(Item *item) const; longlong Item_val_int_unsigned_typecast(Item *item) const; String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; @@ -2156,18 +3458,13 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + bool Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, MYSQL_TIME *, - ulonglong fuzzydate) const; - String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; - double Item_func_min_max_val_real(Item_func_min_max *) const; - longlong Item_func_min_max_val_int(Item_func_min_max *) const; - my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, - my_decimal *) const; - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const; bool Item_func_between_fix_length_and_dec(Item_func_between *func) const; - longlong Item_func_between_val_int(Item_func_between *func) const; bool Item_func_in_fix_comparator_compatible_types(THD *thd, Item_func_in *) const; bool Item_func_round_fix_length_and_dec(Item_func_round *) const; @@ -2185,13 +3482,14 @@ public: class Type_handler_string_result: public Type_handler { - uint Item_temporal_precision(Item *item, bool is_time) const; + uint Item_temporal_precision(THD *thd, Item *item, bool is_time) const; public: Item_result result_type() const { return STRING_RESULT; } Item_result cmp_type() const { return STRING_RESULT; } CHARSET_INFO *charset_for_protocol(const Item *item) const; virtual ~Type_handler_string_result() {} const Type_handler *type_handler_for_comparison() const; + int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const; const Type_handler * type_handler_adjusted_to_max_octet_length(uint max_octet_length, CHARSET_INFO *cs) const; @@ -2211,16 +3509,21 @@ public: const Schema_specification_st *schema) const; uint32 max_display_length(const Item *item) const; - uint Item_time_precision(Item *item) const + bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const; + bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const; + uint Item_time_precision(THD *thd, Item *item) const { - return Item_temporal_precision(item, true); + return Item_temporal_precision(thd, item, true); } - uint Item_datetime_precision(Item *item) const + uint Item_datetime_precision(THD *thd, Item *item) const { - return Item_temporal_precision(item, false); + return Item_temporal_precision(thd, item, false); } uint Item_decimal_precision(const Item *item) const; - bool Item_save_in_value(Item *item, st_value *value) const; + void Item_update_null_value(Item *item) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; void Item_param_setup_conversion(THD *thd, Item_param *) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; @@ -2258,7 +3561,8 @@ public: bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const; bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const; bool Item_val_bool(Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + bool Item_get_date(THD *thd, Item *item, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const; longlong Item_val_int_signed_typecast(Item *item) const; longlong Item_val_int_unsigned_typecast(Item *item) const; String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; @@ -2271,16 +3575,17 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + bool Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, MYSQL_TIME *, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; double Item_func_min_max_val_real(Item_func_min_max *) const; longlong Item_func_min_max_val_int(Item_func_min_max *) const; my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *) const; - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const; bool Item_func_between_fix_length_and_dec(Item_func_between *func) const; longlong Item_func_between_val_int(Item_func_between *func) const; bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const; @@ -2357,6 +3662,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; }; @@ -2391,6 +3703,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; }; @@ -2425,11 +3744,29 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; }; +class Type_handler_bool: public Type_handler_long +{ + static const Name m_name_bool; +public: + const Name name() const { return m_name_bool; } + bool is_bool_type() const { return true; } + void Item_update_null_value(Item *item) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const; +}; + + class Type_handler_longlong: public Type_handler_general_purpose_int { static const Name m_name_longlong; @@ -2463,6 +3800,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; }; @@ -2508,6 +3852,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -2527,6 +3878,9 @@ public: Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; bool Column_definition_fix_attributes(Column_definition *c) const; + void Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *c, + const Field *field) const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, ulonglong table_flags) const @@ -2535,8 +3889,20 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + bool Item_get_date(THD *thd, Item *item, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const; + bool Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *item, + MYSQL_TIME *to, + date_mode_t fuzzydate) const; }; @@ -2577,6 +3943,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const; }; @@ -2607,6 +3980,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; }; @@ -2639,6 +4019,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; }; @@ -2655,8 +4042,12 @@ public: { return MYSQL_TIMESTAMP_TIME; } + Item_literal *create_literal_item(THD *thd, const char *str, size_t length, + CHARSET_INFO *cs, bool send_error) const; Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const; + bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const; uint Item_decimal_scale(const Item *item) const { return Item_decimal_scale_with_seconds(item); @@ -2667,12 +4058,15 @@ public: return Item_divisor_precision_increment_with_seconds(item); } const Type_handler *type_handler_for_comparison() const; + int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const; + void Column_definition_implicit_upgrade(Column_definition *c) const; bool Column_definition_fix_attributes(Column_definition *c) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { return Item_send_time(item, protocol, buf); } + void Item_update_null_value(Item *item) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; String *print_item_value(THD *thd, Item *item, String *str) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; @@ -2690,11 +4084,18 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + bool Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, MYSQL_TIME *, - ulonglong fuzzydate) const; - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const; + longlong Item_func_between_val_int(Item_func_between *func) const; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; bool set_comparator_func(Arg_comparator *cmp) const; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; @@ -2711,6 +4112,7 @@ class Type_handler_time: public Type_handler_time_common public: static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; } virtual ~Type_handler_time() {} + const Name version() const { return m_version_mariadb53; } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -2722,6 +4124,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -2729,6 +4138,7 @@ class Type_handler_time2: public Type_handler_time_common { public: virtual ~Type_handler_time2() {} + const Name version() const { return m_version_mysql56; } enum_field_types real_field_type() const { return MYSQL_TYPE_TIME2; } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, @@ -2741,6 +4151,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -2748,16 +4165,23 @@ class Type_handler_temporal_with_date: public Type_handler_temporal_result { public: virtual ~Type_handler_temporal_with_date() {} - bool Item_save_in_value(Item *item, st_value *value) const; + Item_literal *create_literal_item(THD *thd, const char *str, size_t length, + CHARSET_INFO *cs, bool send_error) const; + bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const; + int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { return Item_send_date(item, protocol, buf); } + void Item_update_null_value(Item *item) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; bool set_comparator_func(Arg_comparator *cmp) const; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const; + longlong Item_func_between_val_int(Item_func_between *func) const; }; @@ -2773,12 +4197,23 @@ public: { return MYSQL_TIMESTAMP_DATE; } + bool cond_notnull_field_isnull_to_field_eq_zero() const + { + return true; + } + Item_literal *create_literal_item(THD *thd, const char *str, size_t length, + CHARSET_INFO *cs, bool send_error) const; Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const; bool Column_definition_fix_attributes(Column_definition *c) const; uint Item_decimal_precision(const Item *item) const; String *print_item_value(THD *thd, Item *item, String *str) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; bool Item_hybrid_func_fix_attributes(THD *thd, const char *name, Type_handler_hybrid_field_type *, @@ -2803,6 +4238,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -2822,6 +4264,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -2837,8 +4286,13 @@ public: { return MYSQL_TIMESTAMP_DATETIME; } + bool cond_notnull_field_isnull_to_field_eq_zero() const + { + return true; + } Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const; + void Column_definition_implicit_upgrade(Column_definition *c) const; bool Column_definition_fix_attributes(Column_definition *c) const; uint Item_decimal_scale(const Item *item) const { @@ -2855,6 +4309,11 @@ public: } String *print_item_value(THD *thd, Item *item, String *str) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; bool Item_hybrid_func_fix_attributes(THD *thd, const char *name, Type_handler_hybrid_field_type *, @@ -2872,6 +4331,7 @@ class Type_handler_datetime: public Type_handler_datetime_common public: static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; } virtual ~Type_handler_datetime() {} + const Name version() const { return m_version_mariadb53; } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -2883,6 +4343,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -2890,6 +4357,7 @@ class Type_handler_datetime2: public Type_handler_datetime_common { public: virtual ~Type_handler_datetime2() {} + const Name version() const { return m_version_mysql56; } enum_field_types real_field_type() const { return MYSQL_TYPE_DATETIME2; } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, @@ -2902,6 +4370,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -2921,6 +4396,7 @@ public: { return true; } + void Column_definition_implicit_upgrade(Column_definition *c) const; bool Column_definition_fix_attributes(Column_definition *c) const; uint Item_decimal_scale(const Item *item) const { @@ -2937,6 +4413,11 @@ public: } String *print_item_value(THD *thd, Item *item, String *str) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; bool Item_hybrid_func_fix_attributes(THD *thd, const char *name, Type_handler_hybrid_field_type *, @@ -2954,6 +4435,7 @@ class Type_handler_timestamp: public Type_handler_timestamp_common public: static uint sec_part_bytes(uint dec) { return m_sec_part_bytes[dec]; } virtual ~Type_handler_timestamp() {} + const Name version() const { return m_version_mariadb53; } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -2965,6 +4447,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -2972,6 +4461,7 @@ class Type_handler_timestamp2: public Type_handler_timestamp_common { public: virtual ~Type_handler_timestamp2() {} + const Name version() const { return m_version_mysql56; } enum_field_types real_field_type() const { return MYSQL_TYPE_TIMESTAMP2; } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, @@ -2986,6 +4476,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -3010,6 +4507,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -3041,6 +4545,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -3056,7 +4567,9 @@ public: const Type_handler *type_handler_for_union(const Item *) const; uint32 max_display_length(const Item *item) const { return 0; } uint32 calc_pack_length(uint32 length) const { return 0; } - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_send(Item *item, Protocol *protocol, st_value *buf) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -3079,6 +4592,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -3115,6 +4635,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -3127,10 +4654,15 @@ public: const Name name() const { return m_name_var_string; } enum_field_types field_type() const { return MYSQL_TYPE_VAR_STRING; } enum_field_types real_field_type() const { return MYSQL_TYPE_STRING; } + enum_field_types traditional_merge_field_type() const + { + return MYSQL_TYPE_VARCHAR; + } const Type_handler *type_handler_for_tmp_table(const Item *item) const { return varstring_type_handler(item); } + void Column_definition_implicit_upgrade(Column_definition *c) const; bool Column_definition_fix_attributes(Column_definition *c) const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, @@ -3173,10 +4705,28 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; bool adjust_spparam_type(Spvar_definition *def, Item *from) const; }; +class Type_handler_hex_hybrid: public Type_handler_varchar +{ + static const Name m_name_hex_hybrid; +public: + virtual ~Type_handler_hex_hybrid() {} + const Name name() const { return m_name_hex_hybrid; } + const Type_handler *cast_to_int_type_handler() const; + const Type_handler *type_handler_for_system_time() const; +}; + + class Type_handler_varchar_compressed: public Type_handler_varchar { public: @@ -3206,6 +4756,9 @@ public: } bool is_param_long_data_type() const { return true; } bool Column_definition_fix_attributes(Column_definition *c) const; + void Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *c, + const Field *field) const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, ulonglong table_flags) const; @@ -3216,6 +4769,13 @@ public: Item **items, uint nitems) const; void Item_param_setup_conversion(THD *thd, Item_param *) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -3317,7 +4877,18 @@ public: const st_value *value) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + void + Column_definition_attributes_frm_pack(const Column_definition_attributes *at, + uchar *buff) const; + bool + Column_definition_attributes_frm_unpack(Column_definition_attributes *attr, + TABLE_SHARE *share, + const uchar *buffer, + LEX_CUSTRING *gis_options) const; bool Column_definition_fix_attributes(Column_definition *c) const; + void Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *c, + const Field *field) const; bool Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *c, @@ -3331,6 +4902,14 @@ public: const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; + bool can_return_int() const { return false; } bool can_return_decimal() const { return false; } bool can_return_real() const { return false; } @@ -3380,6 +4959,9 @@ public: Type_handler_hybrid_field_type *, Type_all_attributes *atrr, Item **items, uint nitems) const; + void Column_definition_reuse_fix_attributes(THD *thd, + Column_definition *c, + const Field *field) const; bool Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *c, @@ -3402,7 +4984,11 @@ class Type_handler_enum: public Type_handler_typelib public: virtual ~Type_handler_enum() {} const Name name() const { return m_name_enum; } - virtual enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; } + enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; } + enum_field_types traditional_merge_field_type() const + { + return MYSQL_TYPE_ENUM; + } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -3414,6 +5000,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -3423,7 +5016,11 @@ class Type_handler_set: public Type_handler_typelib public: virtual ~Type_handler_set() {} const Name name() const { return m_name_set; } - virtual enum_field_types real_field_type() const { return MYSQL_TYPE_SET; } + enum_field_types real_field_type() const { return MYSQL_TYPE_SET; } + enum_field_types traditional_merge_field_type() const + { + return MYSQL_TYPE_SET; + } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -3435,6 +5032,13 @@ public: const Record_addr &addr, const Type_all_attributes &attr, TABLE *table) const; + Field *make_table_field_from_def(TABLE_SHARE *share, + MEM_ROOT *mem_root, + const LEX_CSTRING *name, + const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const; }; @@ -3535,12 +5139,14 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_set type_handler_set; extern MYSQL_PLUGIN_IMPORT Type_handler_string type_handler_string; extern MYSQL_PLUGIN_IMPORT Type_handler_var_string type_handler_var_string; extern MYSQL_PLUGIN_IMPORT Type_handler_varchar type_handler_varchar; +extern MYSQL_PLUGIN_IMPORT Type_handler_hex_hybrid type_handler_hex_hybrid; extern MYSQL_PLUGIN_IMPORT Type_handler_tiny_blob type_handler_tiny_blob; extern MYSQL_PLUGIN_IMPORT Type_handler_medium_blob type_handler_medium_blob; extern MYSQL_PLUGIN_IMPORT Type_handler_long_blob type_handler_long_blob; extern MYSQL_PLUGIN_IMPORT Type_handler_blob type_handler_blob; +extern MYSQL_PLUGIN_IMPORT Type_handler_bool type_handler_bool; extern MYSQL_PLUGIN_IMPORT Type_handler_tiny type_handler_tiny; extern MYSQL_PLUGIN_IMPORT Type_handler_short type_handler_short; extern MYSQL_PLUGIN_IMPORT Type_handler_int24 type_handler_int24; @@ -3553,6 +5159,7 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_newdecimal type_handler_newdecimal; extern MYSQL_PLUGIN_IMPORT Type_handler_olddecimal type_handler_olddecimal; extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year; +extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year2; extern MYSQL_PLUGIN_IMPORT Type_handler_newdate type_handler_newdate; extern MYSQL_PLUGIN_IMPORT Type_handler_date type_handler_date; extern MYSQL_PLUGIN_IMPORT Type_handler_time type_handler_time; diff --git a/sql/sql_type_int.h b/sql/sql_type_int.h index 1eda5651df5..6c88222fed0 100644 --- a/sql/sql_type_int.h +++ b/sql/sql_type_int.h @@ -1,5 +1,4 @@ -/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. - Copyright (c) 2011, 2016, MariaDB +/* Copyright (c) 2018, 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 @@ -18,17 +17,44 @@ #define SQL_TYPE_INT_INCLUDED -// A longlong/ulonglong hybrid. Good to store results of val_int(). -class Longlong_hybrid +class Null_flag +{ +protected: + bool m_is_null; +public: + bool is_null() const { return m_is_null; } + Null_flag(bool is_null) :m_is_null(is_null) { } +}; + + +class Longlong { protected: longlong m_value; +public: + longlong value() const { return m_value; } + Longlong(longlong nr) :m_value(nr) { } +}; + + +class Longlong_null: public Longlong, public Null_flag +{ +public: + Longlong_null(longlong nr, bool is_null) + :Longlong(nr), Null_flag(is_null) + { } +}; + + +// A longlong/ulonglong hybrid. Good to store results of val_int(). +class Longlong_hybrid: public Longlong +{ +protected: bool m_unsigned; public: Longlong_hybrid(longlong nr, bool unsigned_flag) - :m_value(nr), m_unsigned(unsigned_flag) + :Longlong(nr), m_unsigned(unsigned_flag) { } - longlong value() const { return m_value; } bool is_unsigned() const { return m_unsigned; } bool neg() const { return m_value < 0 && !m_unsigned; } ulonglong abs() const diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 6368ed8afd8..1910ad0f83e 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -68,7 +68,7 @@ void select_unit::change_select() curr_sel= current_select_number; /* New SELECT processing starts */ DBUG_ASSERT(table->file->inited == 0); - step= thd->lex->current_select->linkage; + step= thd->lex->current_select->get_linkage(); switch (step) { case INTERSECT_TYPE: @@ -248,7 +248,7 @@ bool select_unit::send_eof() { if (step != INTERSECT_TYPE || (thd->lex->current_select->next_select() && - thd->lex->current_select->next_select()->linkage == INTERSECT_TYPE)) + thd->lex->current_select->next_select()->get_linkage() == INTERSECT_TYPE)) { /* it is not INTESECT or next SELECT in the sequence is INTERSECT so no @@ -752,11 +752,11 @@ bool st_select_lex_unit::join_union_type_attributes(THD *thd_arg, been fixed yet. An Item_type_holder must be created based on a fixed Item, so use the inner Item instead. */ - DBUG_ASSERT(item_tmp->fixed || + DBUG_ASSERT(item_tmp->is_fixed() || (item_tmp->type() == Item::REF_ITEM && ((Item_ref *)(item_tmp))->ref_type() == Item_ref::OUTER_REF)); - if (!item_tmp->fixed) + if (!item_tmp->is_fixed()) item_tmp= item_tmp->real_item(); holders[holder_pos].add_argument(item_tmp); } @@ -1419,7 +1419,7 @@ bool st_select_lex_unit::exec() union_result->change_select(); if (fake_select_lex) { - if (sl != &thd->lex->select_lex) + if (sl != thd->lex->first_select_lex()) fake_select_lex->uncacheable|= sl->uncacheable; else fake_select_lex->uncacheable= 0; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 7a496172aed..27bd6f04670 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -318,7 +318,7 @@ int mysql_update(THD *thd, SQL_SELECT *select= NULL; SORT_INFO *file_sort= 0; READ_RECORD info; - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; @@ -375,7 +375,7 @@ int mysql_update(THD *thd, table->covering_keys= table->s->keys_in_use; table->quick_keys.clear_all(); - query_plan.select_lex= &thd->lex->select_lex; + query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Force privilege re-checking for views after they have been opened. */ @@ -977,7 +977,7 @@ update_begin: myf flags= 0; if (table->file->is_fatal_error(error, HA_CHECK_ALL)) - flags|= ME_FATALERROR; /* Other handler errors are fatal */ + flags|= ME_FATAL; /* Other handler errors are fatal */ prepare_record_for_error_message(error, table); table->file->print_error(error,MYF(flags)); @@ -1088,7 +1088,7 @@ update_begin: { /* purecov: begin inspected */ prepare_record_for_error_message(loc_error, table); - table->file->print_error(loc_error,MYF(ME_FATALERROR)); + table->file->print_error(loc_error,MYF(ME_FATAL)); error= 1; /* purecov: end */ } @@ -1246,7 +1246,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, TABLE *table= table_list->table; #endif List<Item> all_fields; - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); DBUG_ENTER("mysql_prepare_update"); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -1523,7 +1523,7 @@ int mysql_multi_update_prepare(THD *thd) LEX *lex= thd->lex; TABLE_LIST *table_list= lex->query_tables; TABLE_LIST *tl; - List<Item> *fields= &lex->select_lex.item_list; + List<Item> *fields= &lex->first_select_lex()->item_list; table_map tables_for_update; bool update_view= 0; /* @@ -1565,14 +1565,15 @@ int mysql_multi_update_prepare(THD *thd) if (mysql_handle_derived(lex, DT_PREPARE)) DBUG_RETURN(TRUE); - if (setup_tables_and_check_access(thd, &lex->select_lex.context, - &lex->select_lex.top_join_list, + if (setup_tables_and_check_access(thd, + &lex->first_select_lex()->context, + &lex->first_select_lex()->top_join_list, table_list, - lex->select_lex.leaf_tables, FALSE, - UPDATE_ACL, SELECT_ACL, FALSE)) + lex->first_select_lex()->leaf_tables, + FALSE, UPDATE_ACL, SELECT_ACL, FALSE)) DBUG_RETURN(TRUE); - if (lex->select_lex.handle_derived(thd->lex, DT_MERGE)) + if (lex->first_select_lex()->handle_derived(thd->lex, DT_MERGE)) DBUG_RETURN(TRUE); if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), @@ -1595,13 +1596,14 @@ int mysql_multi_update_prepare(THD *thd) thd->table_map_for_update= tables_for_update= get_table_map(fields); - if (unsafe_key_update(lex->select_lex.leaf_tables, tables_for_update)) + if (unsafe_key_update(lex->first_select_lex()->leaf_tables, + tables_for_update)) DBUG_RETURN(true); /* Setup timestamp handling and locking mode */ - List_iterator<TABLE_LIST> ti(lex->select_lex.leaf_tables); + List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables); while ((tl= ti++)) { TABLE *table= tl->table; @@ -1694,7 +1696,7 @@ int mysql_multi_update_prepare(THD *thd) Check that we are not using table that we are updating, but we should skip all tables of UPDATE SELECT itself */ - lex->select_lex.exclude_from_table_unique_test= TRUE; + lex->first_select_lex()->exclude_from_table_unique_test= TRUE; /* We only need SELECT privilege for columns in the values list */ ti.rewind(); while ((tl= ti++)) @@ -1716,7 +1718,7 @@ int mysql_multi_update_prepare(THD *thd) Set exclude_from_table_unique_test value back to FALSE. It is needed for further check in multi_update::prepare whether to use record cache. */ - lex->select_lex.exclude_from_table_unique_test= FALSE; + lex->first_select_lex()->exclude_from_table_unique_test= FALSE; if (lex->save_prep_leaf_tables()) DBUG_RETURN(TRUE); @@ -1745,7 +1747,7 @@ bool mysql_multi_update(THD *thd, DBUG_ENTER("mysql_multi_update"); if (!(*result= new (thd->mem_root) multi_update(thd, table_list, - &thd->lex->select_lex.leaf_tables, + &thd->lex->first_select_lex()->leaf_tables, fields, values, handle_duplicates, ignore))) { @@ -2248,11 +2250,11 @@ int multi_update::prepare2(JOIN *join) { if (item_rowid_table(*it2) != tbl) continue; - Item *fld= new (thd->mem_root) - Item_field(thd, (*it)->get_tmp_table_field()); + Item_field *fld= new (thd->mem_root) + Item_field(thd, (*it)->get_tmp_table_field()); if (!fld) return 1; - fld->set_result_field((*it2)->get_tmp_table_field()); + fld->result_field= (*it2)->get_tmp_table_field(); *it2= fld; } } @@ -2384,7 +2386,7 @@ int multi_update::send_data(List<Item> ¬_used_values) myf flags= 0; if (table->file->is_fatal_error(error, HA_CHECK_ALL)) - flags|= ME_FATALERROR; /* Other handler errors are fatal */ + flags|= ME_FATAL; /* Other handler errors are fatal */ prepare_record_for_error_message(error, table); table->file->print_error(error,MYF(flags)); @@ -2539,17 +2541,10 @@ int multi_update::do_updates() not its dependencies */ while(TABLE *tbl= check_opt_it++) - { - if (tbl->vcol_set) - { - bitmap_clear_all(tbl->vcol_set); - for (Field **vf= tbl->vfield; *vf; vf++) - { + if (Field **vf= tbl->vfield) + for (; *vf; vf++) if (bitmap_is_set(tbl->read_set, (*vf)->field_index)) - tbl->mark_virtual_col(*vf); - } - } - } + (*vf)->vcol_info->expr->walk(&Item::register_field_in_read_map, 1, 0); for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local) { @@ -2639,10 +2634,10 @@ int multi_update::do_updates() uint field_num= 0; do { - if (unlikely((local_error= - tbl->file->ha_rnd_pos(tbl->record[0], - (uchar *) tmp_table-> - field[field_num]->ptr)))) + String rowid; + tmp_table->field[field_num]->val_str(&rowid); + if (unlikely((local_error= tbl->file->ha_rnd_pos(tbl->record[0], + (uchar*)rowid.ptr())))) { err_table= tbl; goto err; @@ -2759,7 +2754,7 @@ int multi_update::do_updates() err: { prepare_record_for_error_message(local_error, err_table); - err_table->file->print_error(local_error,MYF(ME_FATALERROR)); + err_table->file->print_error(local_error,MYF(ME_FATAL)); } err2: diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 07230b2205b..e475a3d3719 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -254,7 +254,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, LEX *lex= thd->lex; /* first table in list is target VIEW name => cut off it */ TABLE_LIST *tbl; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); SELECT_LEX *sl; bool res= TRUE; DBUG_ENTER("create_view_precheck"); @@ -323,7 +323,6 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, } } - if (&lex->select_lex != lex->all_selects_list) { /* check tables of subqueries */ for (tbl= tables; tbl; tbl= tbl->next_global) @@ -399,7 +398,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, TABLE_LIST *view= lex->unlink_first_table(&link_to_local); TABLE_LIST *tables= lex->query_tables; TABLE_LIST *tbl; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); SELECT_LEX *sl; SELECT_LEX_UNIT *unit= &lex->unit; bool res= FALSE; @@ -711,9 +710,10 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, lex->link_first_table_back(view, link_to_local); DBUG_RETURN(0); - -WSREP_ERROR_LABEL: - res= TRUE; +#ifdef WITH_WSREP +wsrep_error_label: + res= true; +#endif err: lex->link_first_table_back(view, link_to_local); @@ -995,7 +995,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, view->algorithm != VIEW_ALGORITHM_TMPTABLE))) { /* TODO: change here when we will support UNIONs */ - for (TABLE_LIST *tbl= lex->select_lex.table_list.first; + for (TABLE_LIST *tbl= lex->first_select_lex()->table_list.first; tbl; tbl= tbl->next_local) { @@ -1114,8 +1114,8 @@ loop_out: UNION */ if (view->updatable_view && - !lex->select_lex.master_unit()->is_unit_op() && - !(lex->select_lex.table_list.first)->next_local && + !lex->first_select_lex()->master_unit()->is_unit_op() && + !(lex->first_select_lex()->table_list.first)->next_local && find_table_in_global_list(lex->query_tables->next_global, &lex->query_tables->db, &lex->query_tables->table_name)) @@ -1162,7 +1162,8 @@ err: bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, bool open_view_no_parse) { - SELECT_LEX *end, *UNINIT_VAR(view_select); + SELECT_LEX_NODE *end; + SELECT_LEX *UNINIT_VAR(view_select); LEX *old_lex, *lex; Query_arena *arena, backup; TABLE_LIST *top_view= table->top_table(); @@ -1361,8 +1362,6 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, lex_start(thd); lex->stmt_lex= old_lex; - view_select= &lex->select_lex; - view_select->select_number= ++thd->lex->stmt_lex->current_select_number; sql_mode_t saved_mode= thd->variables.sql_mode; /* switch off modes which can prevent normal parsing of VIEW @@ -1397,6 +1396,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, parse_status= parse_sql(thd, & parser_state, table->view_creation_ctx); + view_select= lex->first_select_lex(); + /* Restore environment. */ if ((old_lex->sql_command == SQLCOM_SHOW_FIELDS) || @@ -1546,7 +1547,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, This may change in future, for example if we enable merging of views with subqueries in select list. */ - view_main_select_tables= lex->select_lex.table_list.first; + view_main_select_tables= lex->first_select_lex()->table_list.first; /* Let us set proper lock type for tables of the view's main @@ -1573,7 +1574,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, /* Fields in this view can be used in upper select in case of merge. */ if (table->select_lex) - table->select_lex->add_where_field(&lex->select_lex); + table->select_lex->add_where_field(lex->first_select_lex()); } /* This method has a dependency on the proper lock type being set, @@ -1595,8 +1596,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query && lex->safe_to_cache_query); /* move SQL_CACHE to whole query */ - if (view_select->options & OPTION_TO_QUERY_CACHE) - old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE; + if (lex->first_select_lex()->options & OPTION_TO_QUERY_CACHE) + old_lex->first_select_lex()->options|= OPTION_TO_QUERY_CACHE; #ifndef NO_EMBEDDED_ACCESS_CHECKS if (table->view_suid) @@ -1678,9 +1679,10 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, tbl->grant.want_privilege= top_view->grant.orig_want_privilege; /* prepare view context */ - lex->select_lex.context.resolve_in_table_list_only(view_main_select_tables); - lex->select_lex.context.outer_context= 0; - lex->select_lex.select_n_having_items+= + lex->first_select_lex()-> + context.resolve_in_table_list_only(view_main_select_tables); + lex->first_select_lex()->context.outer_context= 0; + lex->first_select_lex()->select_n_having_items+= table->select_lex->select_n_having_items; table->where= view_select->where; @@ -1691,12 +1693,13 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, */ if (!table->select_lex->master_unit()->is_unit_op() && table->select_lex->order_list.elements == 0) - table->select_lex->order_list.push_back(&lex->select_lex.order_list); + table->select_lex->order_list. + push_back(&lex->first_select_lex()->order_list); else { if (old_lex->sql_command == SQLCOM_SELECT && (old_lex->describe & DESCRIBE_EXTENDED) && - lex->select_lex.order_list.elements && + lex->first_select_lex()->order_list.elements && !table->select_lex->master_unit()->is_unit_op()) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, @@ -1731,7 +1734,11 @@ ok: lex->unit.include_down(table->select_lex); lex->unit.slave= view_select; // fix include_down initialisation /* global SELECT list linking */ - end= view_select; // primary SELECT_LEX is always last + /* + The primary SELECT_LEX is always last (because parsed first) if WITH not + used, otherwise it is good start point for last element finding + */ + for (end= view_select; end->link_next; end= end->link_next); end->link_next= old_lex->all_selects_list; old_lex->all_selects_list->link_prev= &end->link_next; old_lex->all_selects_list= lex->all_selects_list; @@ -1916,7 +1923,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) */ if ((!view->view && !view->belong_to_view) || thd->lex->sql_command == SQLCOM_INSERT || - thd->lex->select_lex.select_limit == 0) + thd->lex->first_select_lex()->select_limit == 0) DBUG_RETURN(FALSE); /* it is normal table or query without LIMIT */ table= view->table; view= view->top_table(); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f47bcfb5022..40caafb32e1 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -491,96 +491,6 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, } /** - @brief Creates a new SELECT_LEX for a UNION branch. - - Sets up and initializes a SELECT_LEX structure for a query once the parser - discovers a UNION token. The current SELECT_LEX is pushed on the stack and - the new SELECT_LEX becomes the current one. - - @param lex The parser state. - - @param is_union_distinct True if the union preceding the new select - statement uses UNION DISTINCT. - - @param is_top_level This should be @c TRUE if the newly created SELECT_LEX - is a non-nested statement. - - @return <code>false</code> if successful, <code>true</code> if an error was - reported. In the latter case parsing should stop. - */ -bool LEX::add_select_to_union_list(bool is_union_distinct, - enum sub_select_type type, - bool is_top_level) -{ - const char *type_name= (type == INTERSECT_TYPE ? "INTERSECT" : - (type == EXCEPT_TYPE ? "EXCEPT" : "UNION")); - /* - Only the last SELECT can have INTO. Since the grammar won't allow INTO in - a nested SELECT, we make this check only when creating a top-level SELECT. - */ - if (is_top_level && result) - { - my_error(ER_WRONG_USAGE, MYF(0), type_name, "INTO"); - return TRUE; - } - if (current_select->order_list.first && !current_select->braces) - { - my_error(ER_WRONG_USAGE, MYF(0), type_name, "ORDER BY"); - return TRUE; - } - - if (current_select->explicit_limit && !current_select->braces) - { - my_error(ER_WRONG_USAGE, MYF(0), type_name, "LIMIT"); - return TRUE; - } - if (current_select->linkage == GLOBAL_OPTIONS_TYPE) - { - thd->parse_error(); - return TRUE; - } - if (!is_union_distinct && (type == INTERSECT_TYPE || type == EXCEPT_TYPE)) - { - my_error(ER_WRONG_USAGE, MYF(0), type_name, "ALL"); - return TRUE; - } - /* - Priority implementation, but also trying to keep things as flat - as possible */ - if (type == INTERSECT_TYPE && - (current_select->linkage != INTERSECT_TYPE && - current_select != current_select->master_unit()->first_select()) - && !(thd->variables.sql_mode & MODE_ORACLE)) - { - /* - This and previous SELECTs should go one level down because of - priority - */ - SELECT_LEX *prev= exclude_last_select(); - if (add_unit_in_brackets(prev)) - return TRUE; - return add_select_to_union_list(is_union_distinct, type, 0); - } - else - { - check_automatic_up(type); - } - /* This counter shouldn't be incremented for UNION parts */ - nest_level--; - if (mysql_new_select(this, 0, NULL)) - return TRUE; - mysql_init_select(this); - current_select->linkage= type; - current_select->with_all_modifier= !is_union_distinct; - if (is_union_distinct) /* UNION DISTINCT - remember position */ - current_select->master_unit()->union_distinct= current_select; - else - DBUG_ASSERT(type == UNION_TYPE); - return FALSE; -} - - -/** Create a separate LEX for each assignment if in SP. If we are in SP we want have own LEX for each assignment. @@ -621,6 +531,7 @@ void sp_create_assignment_lex(THD *thd, bool no_lookahead) lex->sphead->m_tmp_query= lip->get_tok_end(); /* Inherit from outer lex. */ lex->option_type= old_lex->option_type; + lex->main_select_push(); } } @@ -680,6 +591,9 @@ bool sp_create_assignment_instr(THD *thd, bool no_lookahead) if (sp->add_instr(i)) return true; } + lex->pop_select(); + if (Lex->check_main_unit_semantics()) + return true; enum_var_type inner_option_type= lex->option_type; if (lex->sphead->restore_lex(thd)) return true; @@ -767,6 +681,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) return v; } + %} %union { int num; @@ -796,6 +711,20 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) uint offset; } sp_cursor_name_and_offset; vers_history_point_t vers_history_point; + struct + { + enum sub_select_type unit_type; + bool distinct; + } unit_operation; + struct + { + SELECT_LEX *first; + SELECT_LEX *prev_last; + } select_list; + SQL_I_List<ORDER> *select_order; + Lex_select_lock select_lock; + Lex_select_limit select_limit; + Lex_order_limit_lock *order_limit_lock; /* pointers */ Create_field *create_field; @@ -841,6 +770,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) handlerton *db_type; st_select_lex *select_lex; + st_select_lex_unit *select_lex_unit; struct p_elem_val *p_elem_value; class Window_frame *window_frame; class Window_frame_bound *window_frame_bound; @@ -849,7 +779,6 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) /* enums */ enum enum_view_suid view_suid; - enum sub_select_type unit_type; enum Condition_information_item::Name cond_info_item_name; enum enum_diag_condition_item_name diag_condition_item_name; enum Diagnostics_information::Which_area diag_area; @@ -888,10 +817,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 56 shift/reduce conflicts. + Currently there are 52 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 56 +%expect 52 /* Comments for TOKENS. @@ -1042,6 +971,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token LEADING /* SQL-2003-R */ %token LEAVE_SYM %token LEFT /* SQL-2003-R */ +%token LEFT_PAREN_ALT /* INTERNAL */ +%token LEFT_PAREN_WITH /* INTERNAL */ +%token LEFT_PAREN_LIKE /* INTERNAL */ %token LEX_HOSTNAME %token LIKE /* SQL-2003-R */ %token LIMIT @@ -1798,7 +1730,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); NCHAR_STRING %type <lex_str_ptr> - opt_table_alias + opt_table_alias_clause + table_alias_clause %type <ident_cli> IDENT @@ -1861,7 +1794,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_temporary all_or_any opt_distinct opt_glimit_clause opt_ignore_leaves fulltext_options union_option opt_not - select_derived_init transaction_access_mode_types + transaction_access_mode_types opt_natural_language_mode opt_query_expansion opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt @@ -1981,11 +1914,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); join_table_list join_table table_factor table_ref esc_table_ref table_primary_ident table_primary_derived - select_derived derived_table_list - select_derived_union - derived_simple_table - derived_query_specification - derived_table_value_constructor + derived_table_list table_reference_list_parens + nested_table_reference_list join_table_parens %type <date_time_type> date_time_type; %type <interval> interval @@ -2019,14 +1949,19 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); UNDERSCORE_CHARSET %type <select_lex> subselect - get_select_lex get_select_lex_derived - simple_table query_specification - query_term_union_not_ready - query_term_union_ready - query_expression_body - select_paren_derived table_value_constructor + simple_table + query_primary + query_primary_parens + select_into_query_specification + + +%type <select_lex_unit> + query_specification_start + query_expression_body + query_expression + query_expression_unit %type <boolfunc2creator> comp_op @@ -2038,11 +1973,28 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type <virtual_column> opt_check_constraint check_constraint virtual_column_func column_default_expr -%type <unit_type> unit_type_decl + +%type <unit_operation> unit_type_decl + +%type <select_lock> + opt_procedure_or_into + opt_select_lock_type + select_lock_type + opt_lock_wait_timeout_new + +%type <select_limit> opt_limit_clause limit_clause limit_options + +%type <order_limit_lock> + query_expression_tail + order_or_limit + opt_order_limit_lock + +%type <select_order> opt_order_clause order_clause order_list %type <NONE> analyze_stmt_command - query verb_clause create change select do drop insert replace insert2 + query verb_clause create change select select_into + do drop insert replace insert2 insert_values update delete truncate rename compound_statement show describe load alter optimize keycache preload flush reset purge begin commit rollback savepoint release @@ -2058,7 +2010,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); assign_to_keycache_parts preload_list preload_list_or_parts preload_keys preload_keys_parts select_item_list select_item values_list no_braces - opt_limit_clause delete_limit_clause fields opt_values values + delete_limit_clause fields opt_values values no_braces_with_names opt_values_with_names values_with_names procedure_list procedure_list2 procedure_item field_def handler opt_generated_always @@ -2079,9 +2031,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan single_multi table_wild_list table_wild_one opt_wild - union_clause union_list - subselect_start opt_and charset - subselect_end select_var_list select_var_list_init help + opt_and charset + select_var_list select_var_list_init help opt_extended_describe shutdown opt_format_json prepare prepare_src execute deallocate @@ -2209,8 +2160,8 @@ rule: <-- starts at col 1 query: END_OF_INPUT { - if (likely(!thd->bootstrap) && - unlikely(!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT))) + if (!thd->bootstrap && + (!(thd->lex->lex_options & OPTION_LEX_FOUND_COMMENT))) my_yyabort_error((ER_EMPTY_QUERY, MYF(0))); thd->lex->sql_command= SQLCOM_EMPTY_QUERY; @@ -2305,6 +2256,7 @@ statement: | rollback | savepoint | select + | select_into | set | signal_stmt | show @@ -2340,6 +2292,8 @@ prepare: if (unlikely(lex->table_or_sp_used())) my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "PREPARE..FROM")); + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; lex->sql_command= SQLCOM_PREPARE; lex->prepared_stmt_name= $2; } @@ -2362,7 +2316,10 @@ execute: lex->prepared_stmt_name= $2; } execute_using - {} + { + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } | EXECUTE_SYM IMMEDIATE_SYM prepare_src { if (unlikely(Lex->table_or_sp_used())) @@ -2371,7 +2328,10 @@ execute: Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE; } execute_using - {} + { + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } ; execute_using: @@ -2658,17 +2618,22 @@ connection_name: /* create a table */ create: - create_or_replace opt_temporary TABLE_SYM opt_if_not_exists table_ident + create_or_replace opt_temporary TABLE_SYM opt_if_not_exists { LEX *lex= thd->lex; lex->create_info.init(); - if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, - $1 | $4))) + if (lex->main_select_push()) + MYSQL_YYABORT; + lex->current_select->parsing_place= BEFORE_OPT_LIST; + if (lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4)) MYSQL_YYABORT; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL, - TL_OPTION_UPDATING, - TL_WRITE, - MDL_EXCLUSIVE))) + } + table_ident + { + LEX *lex= thd->lex; + if (!lex->first_select_lex()-> + add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) MYSQL_YYABORT; lex->alter_info.reset(); /* @@ -2683,7 +2648,6 @@ create: create_body { LEX *lex= thd->lex; - lex->current_select= &lex->select_lex; if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && !lex->create_info.db_type) { @@ -2692,22 +2656,24 @@ create: ER_WARN_USING_OTHER_HANDLER, ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), hton_name(lex->create_info.db_type)->str, - $5->table.str); + $6->table.str); } create_table_set_open_action_and_adjust_tables(lex); + Lex->pop_select(); //main select } | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident { LEX *lex= thd->lex; + if (Lex->main_select_push()) + MYSQL_YYABORT; lex->create_info.init(); if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4))) MYSQL_YYABORT; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL, - TL_OPTION_UPDATING, - TL_WRITE, - MDL_EXCLUSIVE))) + if (!lex->first_select_lex()-> + add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) MYSQL_YYABORT; /* @@ -2730,8 +2696,9 @@ create: if (unlikely(lex->create_info.seq_create_info->check_and_adjust(1))) { my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), - lex->select_lex.table_list.first->db.str, - lex->select_lex.table_list.first->table_name.str); + lex->first_select_lex()->table_list.first->db.str, + lex->first_select_lex()->table_list.first-> + table_name.str); MYSQL_YYABORT; } @@ -2744,10 +2711,8 @@ create: Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; Lex->create_info.sequence= 1; - lex->current_select= &lex->select_lex; - if (unlikely((lex->create_info.used_fields & - HA_CREATE_USED_ENGINE) && - !lex->create_info.db_type)) + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) { lex->create_info.use_default_db_type(thd); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -2757,44 +2722,69 @@ create: $5->table.str); } create_table_set_open_action_and_adjust_tables(lex); + Lex->pop_select(); //main select } - | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident + | create_or_replace opt_unique INDEX_SYM opt_if_not_exists + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + ident opt_key_algorithm_clause ON table_ident { - if (unlikely(Lex->add_create_index_prepare($8))) + if (Lex->add_create_index_prepare($9)) MYSQL_YYABORT; - if (unlikely(Lex->add_create_index($2, &$5, $6, $1 | $4))) + if (Lex->add_create_index($2, &$6, $7, $1 | $4)) MYSQL_YYABORT; } '(' key_list ')' opt_lock_wait_timeout normal_key_options - opt_index_lock_algorithm { } - | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident + opt_index_lock_algorithm + { + Lex->pop_select(); //main select + } + | create_or_replace fulltext INDEX_SYM + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + opt_if_not_exists ident ON table_ident { - if (unlikely(Lex->add_create_index_prepare($7))) + if (Lex->add_create_index_prepare($8)) MYSQL_YYABORT; - if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, - $1 | $4))) + if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5)) MYSQL_YYABORT; } '(' key_list ')' opt_lock_wait_timeout fulltext_key_options - opt_index_lock_algorithm { } - | create_or_replace spatial INDEX_SYM opt_if_not_exists ident + opt_index_lock_algorithm + { + Lex->pop_select(); //main select + } + | create_or_replace spatial INDEX_SYM + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + opt_if_not_exists ident ON table_ident { - if (unlikely(Lex->add_create_index_prepare($7))) + if (Lex->add_create_index_prepare($8)) MYSQL_YYABORT; - if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, - $1 | $4))) + if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5)) MYSQL_YYABORT; } '(' key_list ')' opt_lock_wait_timeout spatial_key_options - opt_index_lock_algorithm { } + opt_index_lock_algorithm + { + Lex->pop_select(); //main select + } | create_or_replace DATABASE opt_if_not_exists ident { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; + if (Lex->main_select_push()) + MYSQL_YYABORT; } opt_create_database_options { @@ -2803,61 +2793,105 @@ create: $1 | $3))) MYSQL_YYABORT; lex->name= $4; + Lex->pop_select(); //main select } | create_or_replace definer_opt opt_view_suid VIEW_SYM opt_if_not_exists table_ident { - if (unlikely(Lex->add_create_view(thd, $1 | $5, - DTYPE_ALGORITHM_UNDEFINED, $3, - $6))) + if (Lex->main_select_push()) + MYSQL_YYABORT; + if (Lex->add_create_view(thd, $1 | $5, + DTYPE_ALGORITHM_UNDEFINED, $3, $6)) MYSQL_YYABORT; } view_list_opt AS view_select - { } + { + Lex->pop_select(); //main select + } | create_or_replace view_algorithm definer_opt opt_view_suid VIEW_SYM opt_if_not_exists table_ident { if (unlikely(Lex->add_create_view(thd, $1 | $6, $2, $4, $7))) MYSQL_YYABORT; + if (Lex->main_select_push()) + MYSQL_YYABORT; } view_list_opt AS view_select - { } + { + Lex->pop_select(); //main select + } | create_or_replace definer_opt TRIGGER_SYM - { Lex->create_info.set($1); } + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } trigger_tail - { } + { + Lex->pop_select(); //main select + } | create_or_replace definer_opt PROCEDURE_SYM - { Lex->create_info.set($1); } + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } sp_tail - { } + { + Lex->pop_select(); //main select + } | create_or_replace definer_opt EVENT_SYM - { Lex->create_info.set($1); } + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } event_tail - { } + { + Lex->pop_select(); //main select + } | create_or_replace definer FUNCTION_SYM { + if (Lex->main_select_push()) + MYSQL_YYABORT; Lex->create_info.set($1); } - sf_tail_not_aggregate - { } + sf_tail + { + Lex->pop_select(); //main select + } | create_or_replace definer AGGREGATE_SYM FUNCTION_SYM { + if (Lex->main_select_push()) + MYSQL_YYABORT; Lex->create_info.set($1); } sf_tail_aggregate - { } + { + Lex->pop_select(); //main select + } | create_or_replace no_definer FUNCTION_SYM - { Lex->create_info.set($1); } + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } create_function_tail - { } + { + Lex->pop_select(); //main select + } | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM { + if (Lex->main_select_push()) + MYSQL_YYABORT; Lex->create_info.set($1); } create_aggregate_function_tail - { } - | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list - opt_require_clause opt_resource_options + { + Lex->pop_select(); //main select + } + | create_or_replace USER_SYM opt_if_not_exists clear_privileges + grant_list opt_require_clause opt_resource_options { if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER, $1 | $3))) @@ -3258,7 +3292,7 @@ clear_privileges: lex->columns.empty(); lex->grant= lex->grant_tot_col= 0; lex->all_privileges= 0; - lex->select_lex.db= null_clex_str; + lex->first_select_lex()->db= null_clex_str; lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; bzero((char *)&(lex->mqh),sizeof(lex->mqh)); @@ -3757,7 +3791,7 @@ sp_hcond: signal_stmt: SIGNAL_SYM signal_value opt_set_signal_information { - if (unlikely(Lex->add_signal_statement(thd, $2))) + if (Lex->add_signal_statement(thd, $2)) MYSQL_YYABORT; } ; @@ -4217,7 +4251,7 @@ assignment_source_expr: $$->sp_lex_in_use= true; $$->set_item_and_free_list($3, thd->free_list); thd->free_list= NULL; - if (unlikely($$->sphead->restore_lex(thd))) + if ($$->sphead->restore_lex(thd)) MYSQL_YYABORT; } ; @@ -4226,6 +4260,7 @@ for_loop_bound_expr: assignment_source_lex { Lex->sphead->reset_lex(thd, $1); + Lex->current_select->parsing_place= FOR_LOOP_BOUND; } expr { @@ -4235,6 +4270,7 @@ for_loop_bound_expr: $$->set_item_and_free_list($3, NULL); if (unlikely($$->sphead->restore_lex(thd))) MYSQL_YYABORT; + Lex->current_select->parsing_place= NO_MATTER; } ; @@ -4468,7 +4504,8 @@ case_stmt_body: { if (unlikely(Lex->case_stmt_action_expr($2))) MYSQL_YYABORT; - if (unlikely(Lex->sphead->restore_lex(thd))) + + if (Lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } simple_when_clause_list @@ -4670,7 +4707,7 @@ while_body: LEX *lex= Lex; if (unlikely(lex->sp_while_loop_expression(thd, $1))) MYSQL_YYABORT; - if (unlikely(lex->sphead->restore_lex(thd))) + if (lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } sp_proc_stmts1 END WHILE_SYM @@ -4693,7 +4730,7 @@ repeat_body: if (unlikely(i == NULL) || unlikely(lex->sphead->add_instr(i))) MYSQL_YYABORT; - if (unlikely(lex->sphead->restore_lex(thd))) + if (lex->sphead->restore_lex(thd)) MYSQL_YYABORT; /* We can shortcut the cont_backpatch here */ i->m_cont_dest= ip+1; @@ -5173,26 +5210,16 @@ size_number: */ create_body: - '(' create_field_list ')' + create_field_list_parens { Lex->create_info.option_list= NULL; } opt_create_table_options opt_create_partitioning opt_create_select {} | opt_create_table_options opt_create_partitioning opt_create_select {} - /* - the following rule is redundant, but there's a shift/reduce - conflict that prevents the rule above from parsing a syntax like - CREATE TABLE t1 (SELECT 1); - */ - | '(' create_select_query_specification ')' - | '(' create_select_query_specification ')' - { Select->set_braces(1);} union_list {} - | '(' create_select_query_specification ')' - { Select->set_braces(1);} union_order_or_limit {} | create_like { Lex->create_info.add(DDL_options_st::OPT_LIKE); - TABLE_LIST *src_table= Lex->select_lex.add_table_to_list(thd, - $1, NULL, 0, TL_READ, MDL_SHARED_READ); + TABLE_LIST *src_table= Lex->first_select_lex()-> + add_table_to_list(thd, $1, NULL, 0, TL_READ, MDL_SHARED_READ); if (unlikely(! src_table)) MYSQL_YYABORT; /* CREATE TABLE ... LIKE is not allowed for views. */ @@ -5202,7 +5229,7 @@ create_body: create_like: LIKE table_ident { $$= $2; } - | '(' LIKE table_ident ')' { $$= $3; } + | LEFT_PAREN_LIKE LIKE table_ident ')' { $$= $3; } ; opt_create_select: @@ -5211,23 +5238,19 @@ opt_create_select: ; create_select_query_expression: - opt_with_clause SELECT_SYM create_select_part2 opt_table_expression - create_select_part4 - { - Select->set_braces(0); - Select->set_with_clause($1); + query_expression + { + if (Lex->parsed_insert_select($1->first_select())) + MYSQL_YYABORT; } - union_clause - | opt_with_clause SELECT_SYM create_select_part2 - create_select_part3_union_not_ready create_select_part4 + | LEFT_PAREN_WITH with_clause query_expression_body ')' { - Select->set_with_clause($1); + SELECT_LEX *first_select= $3->first_select(); + $3->set_with_clause($2); + $2->attach_to(first_select); + if (Lex->parsed_insert_select(first_select)) + MYSQL_YYABORT; } - | '(' create_select_query_specification ')' - | '(' create_select_query_specification ')' - { Select->set_braces(1);} union_list {} - | '(' create_select_query_specification ')' - { Select->set_braces(1);} union_order_or_limit {} ; opt_create_partitioning: @@ -5310,13 +5333,17 @@ partition_entry: thd->parse_error(ER_PARTITION_ENTRY_ERROR); MYSQL_YYABORT; } - DBUG_ASSERT(Lex->part_info->table); + if (Lex->main_select_push()) + MYSQL_YYABORT; /* We enter here when opening the frm file to translate partition info string into part_info data structure. */ } - partition {} + partition + { + Lex->pop_select(); //main select + } ; partition: @@ -5927,7 +5954,7 @@ opt_versioning_rotation: | INTERVAL_SYM expr interval opt_versioning_interval_start { partition_info *part_info= Lex->part_info; - if (unlikely(part_info->vers_set_interval($2, $3, $4))) + if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4))) { my_error(ER_PART_WRONG_VALUE, MYF(0), Lex->create_last_non_select_table->table_name.str, @@ -5970,56 +5997,6 @@ opt_versioning_interval_start: End of partition parser part */ -create_select_query_specification: - opt_with_clause SELECT_SYM create_select_part2 create_select_part3 - create_select_part4 - { - Select->set_with_clause($1); - } - ; - -create_select_part2: - { - LEX *lex=Lex; - if (lex->sql_command == SQLCOM_INSERT) - lex->sql_command= SQLCOM_INSERT_SELECT; - else if (lex->sql_command == SQLCOM_REPLACE) - lex->sql_command= SQLCOM_REPLACE_SELECT; - /* - The following work only with the local list, the global list - is created correctly in this case - */ - lex->current_select->table_list.save_and_clear(&lex->save_list); - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; - } - select_options select_item_list - { - Select->parsing_place= NO_MATTER; - } - ; - -create_select_part3: - opt_table_expression - | create_select_part3_union_not_ready - ; - -create_select_part3_union_not_ready: - table_expression order_or_limit - | order_or_limit - ; - -create_select_part4: - opt_select_lock_type - { - /* - The following work only with the local list, the global list - is created correctly in this case - */ - Lex->current_select->table_list.push_front(&Lex->save_list); - } - ; - opt_as: /* empty */ {} | AS {} @@ -6237,7 +6214,7 @@ create_table_option: } | UNION_SYM opt_equal { - Lex->select_lex.table_list.save_and_clear(&Lex->save_list); + Lex->first_select_lex()->table_list.save_and_clear(&Lex->save_list); } '(' opt_table_list ')' { @@ -6246,8 +6223,8 @@ create_table_option: from the global list. */ LEX *lex=Lex; - lex->create_info.merge_list= lex->select_lex.table_list; - lex->select_lex.table_list= lex->save_list; + lex->create_info.merge_list= lex->first_select_lex()->table_list; + lex->first_select_lex()->table_list= lex->save_list; /* When excluding union list from the global list we assume that elements of the former immediately follow elements which represent @@ -6448,6 +6425,13 @@ create_field_list: } ; +create_field_list_parens: + LEFT_PAREN_ALT field_list ')' + { + Lex->create_last_non_select_table= Lex->last_table(); + } + ; + field_list: field_list_item | field_list ',' field_list_item @@ -6742,6 +6726,8 @@ parse_vcol_expr: Prevent the end user from invoking this command. */ MYSQL_YYABORT_UNLESS(Lex->parse_vcol_expr); + if (Lex->main_select_push()) + MYSQL_YYABORT; } expr { @@ -6749,14 +6735,15 @@ parse_vcol_expr: if (unlikely(!v)) MYSQL_YYABORT; Lex->last_field->vcol_info= v; + Lex->pop_select(); //main select } ; parenthesized_expr: - subselect + remember_tok_start + query_expression { - $$= new (thd->mem_root) Item_singlerow_subselect(thd, $1); - if (unlikely($$ == NULL)) + if (!($$= Lex->create_item_query_expression(thd, $1, $2))) MYSQL_YYABORT; } | expr @@ -7673,23 +7660,25 @@ alter: Lex->name= null_clex_str; Lex->table_type= TABLE_TYPE_UNKNOWN; Lex->sql_command= SQLCOM_ALTER_TABLE; - Lex->duplicates= DUP_ERROR; - Lex->select_lex.init_order(); + Lex->duplicates= DUP_ERROR; + Lex->first_select_lex()->order_list.empty(); Lex->create_info.init(); Lex->create_info.row_type= ROW_TYPE_NOT_USED; Lex->alter_info.reset(); Lex->no_write_to_binlog= 0; Lex->create_info.storage_media= HA_SM_DEFAULT; + if (Lex->main_select_push()) + MYSQL_YYABORT; DBUG_ASSERT(!Lex->m_sql_cmd); } alter_options TABLE_SYM table_ident opt_lock_wait_timeout { - if (unlikely(!Lex->select_lex.add_table_to_list(thd, $5, NULL, - TL_OPTION_UPDATING, - TL_READ_NO_INSERT, - MDL_SHARED_UPGRADABLE))) + if (!Lex->first_select_lex()-> + add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, + TL_READ_NO_INSERT, MDL_SHARED_UPGRADABLE)) MYSQL_YYABORT; - Lex->select_lex.db= (Lex->select_lex.table_list.first)->db; + Lex->first_select_lex()->db= + (Lex->first_select_lex()->table_list.first)->db; Lex->create_last_non_select_table= Lex->last_table(); } alter_commands @@ -7701,11 +7690,14 @@ alter: if (unlikely(Lex->m_sql_cmd == NULL)) MYSQL_YYABORT; } + Lex->pop_select(); //main select } | ALTER DATABASE ident_or_empty { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; + if (Lex->main_select_push()) + MYSQL_YYABORT; } create_database_options { @@ -7715,6 +7707,7 @@ alter: if (lex->name.str == NULL && unlikely(lex->copy_db_to(&lex->name))) MYSQL_YYABORT; + Lex->pop_select(); //main select } | ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM { @@ -7730,6 +7723,8 @@ alter: if (unlikely(lex->sphead)) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE")); + if (Lex->main_select_push()) + MYSQL_YYABORT; lex->sp_chistics.init(); } sp_a_chistics @@ -7738,6 +7733,9 @@ alter: lex->sql_command= SQLCOM_ALTER_PROCEDURE; lex->spname= $3; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } | ALTER FUNCTION_SYM sp_name { @@ -7745,6 +7743,8 @@ alter: if (unlikely(lex->sphead)) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); + if (Lex->main_select_push()) + MYSQL_YYABORT; lex->sp_chistics.init(); } sp_a_chistics @@ -7753,14 +7753,23 @@ alter: lex->sql_command= SQLCOM_ALTER_FUNCTION; lex->spname= $3; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } | ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident { - if (unlikely(Lex->add_alter_view(thd, $2, $4, $6))) + if (Lex->main_select_push()) + MYSQL_YYABORT; + if (Lex->add_alter_view(thd, $2, $4, $6)) MYSQL_YYABORT; } view_list_opt AS view_select - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } | ALTER definer_opt opt_view_suid VIEW_SYM table_ident /* We have two separate rules for ALTER VIEW rather that @@ -7768,14 +7777,22 @@ alter: with the ALTER EVENT below. */ { - if (unlikely(Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5))) + if (Lex->main_select_push()) + MYSQL_YYABORT; + if (Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5)) MYSQL_YYABORT; } view_list_opt AS view_select - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } | ALTER definer_opt remember_name EVENT_SYM sp_name { - /* + if (Lex->main_select_push()) + MYSQL_YYABORT; + /* It is safe to use Lex->spname because ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO is not allowed. Lex->spname is used in the case of RENAME TO @@ -7807,6 +7824,8 @@ alter: */ Lex->sql_command= SQLCOM_ALTER_EVENT; Lex->stmt_definition_end= (char*)YYLIP->get_cpp_ptr(); + + Lex->pop_select(); //main select } | ALTER TABLESPACE alter_tablespace_info { @@ -7850,16 +7869,17 @@ alter: lex->create_info.init(); lex->no_write_to_binlog= 0; DBUG_ASSERT(!lex->m_sql_cmd); + if (Lex->main_select_push()) + MYSQL_YYABORT; } table_ident { LEX *lex= Lex; - if (unlikely(!(lex->create_info.seq_create_info= - new (thd->mem_root) sequence_definition())) || - unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL, - TL_OPTION_SEQUENCE, - TL_WRITE, - MDL_EXCLUSIVE))) + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition()) || + !lex->first_select_lex()-> + add_table_to_list(thd, $5, NULL, TL_OPTION_SEQUENCE, + TL_WRITE, MDL_EXCLUSIVE)) MYSQL_YYABORT; } sequence_defs @@ -7868,6 +7888,9 @@ alter: Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_sequence($3); if (unlikely(Lex->m_sql_cmd == NULL)) MYSQL_YYABORT; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } ; @@ -8017,16 +8040,17 @@ alter_commands: WITH TABLE_SYM table_ident have_partitioning { LEX *lex= thd->lex; - lex->select_lex.db= $6->db; - if (lex->select_lex.db.str == NULL && - unlikely(lex->copy_db_to(&lex->select_lex.db))) + lex->first_select_lex()->db=$6->db; + if (lex->first_select_lex()->db.str == NULL && + lex->copy_db_to(&lex->first_select_lex()->db)) + { MYSQL_YYABORT; + } lex->name= $6->table; lex->alter_info.partition_flags|= ALTER_PARTITION_EXCHANGE; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $6, NULL, - TL_OPTION_UPDATING, - TL_READ_NO_INSERT, - MDL_SHARED_NO_WRITE))) + if (!lex->first_select_lex()-> + add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING, + TL_READ_NO_INSERT, MDL_SHARED_NO_WRITE)) MYSQL_YYABORT; DBUG_ASSERT(!lex->m_sql_cmd); lex->m_sql_cmd= new (thd->mem_root) @@ -8265,9 +8289,9 @@ alter_list_item: | RENAME opt_to table_ident { LEX *lex=Lex; - lex->select_lex.db= $3->db; - if (lex->select_lex.db.str == NULL && - unlikely(lex->copy_db_to(&lex->select_lex.db))) + lex->first_select_lex()->db= $3->db; + if (lex->first_select_lex()->db.str == NULL && + lex->copy_db_to(&lex->first_select_lex()->db)) MYSQL_YYABORT; if (unlikely(check_table_name($3->table.str,$3->table.length, FALSE)) || @@ -8962,8 +8986,8 @@ adm_partition: cache_keys_spec: { - Lex->select_lex.alloc_index_hints(thd); - Select->set_index_hint_type(INDEX_HINT_USE, + Lex->first_select_lex()->alloc_index_hints(thd); + Select->set_index_hint_type(INDEX_HINT_USE, INDEX_HINT_MASK_ALL); } cache_key_list_or_empty @@ -8984,217 +9008,218 @@ opt_ignore_leaves: Select : retrieve data from table */ - select: - opt_with_clause select_init - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_SELECT; - lex->current_select->set_with_clause($1); - } - ; - -select_init: - SELECT_SYM select_options_and_item_list select_init3 - | table_value_constructor - | table_value_constructor union_list - | table_value_constructor union_order_or_limit - | '(' select_paren ')' - | '(' select_paren ')' union_list - | '(' select_paren ')' union_order_or_limit - ; - -union_list_part2: - SELECT_SYM select_options_and_item_list select_init3_union_query_term - | table_value_constructor - | table_value_constructor union_list - | table_value_constructor union_order_or_limit - | '(' select_paren_union_query_term ')' - | '(' select_paren_union_query_term ')' union_list - | '(' select_paren_union_query_term ')' union_order_or_limit - ; - -select_paren: + query_expression_body { - Lex->current_select->set_braces(true); + if (Lex->push_select($1->fake_select_lex ? + $1->fake_select_lex : + $1->first_select())) + MYSQL_YYABORT; } - table_value_constructor + opt_procedure_or_into { - DBUG_ASSERT(Lex->current_select->braces); + Lex->pop_select(); + if ($1->set_lock_to_the_last_select($3)) + MYSQL_YYABORT; + if (Lex->select_finalize($1)) + MYSQL_YYABORT; } - | + | with_clause query_expression_body { - /* - In order to correctly parse UNION's global ORDER BY we need to - set braces before parsing the clause. - */ - Lex->current_select->set_braces(true); + if (Lex->push_select($2->fake_select_lex ? + $2->fake_select_lex : + $2->first_select())) + MYSQL_YYABORT; } - SELECT_SYM select_options_and_item_list select_part3 - opt_select_lock_type + opt_procedure_or_into { - DBUG_ASSERT(Lex->current_select->braces); + Lex->pop_select(); + $2->set_with_clause($1); + $1->attach_to($2->first_select()); + if ($2->set_lock_to_the_last_select($4)) + MYSQL_YYABORT; + if (Lex->select_finalize($2)) + MYSQL_YYABORT; } - | '(' select_paren ')' ; -select_paren_union_query_term: + +select_into: + select_into_query_specification { - /* - In order to correctly parse UNION's global ORDER BY we need to - set braces before parsing the clause. - */ - Lex->current_select->set_braces(true); + if (Lex->push_select($1)) + MYSQL_YYABORT; } - SELECT_SYM select_options_and_item_list select_part3_union_query_term - opt_select_lock_type + opt_order_limit_lock { - DBUG_ASSERT(Lex->current_select->braces); - } - | '(' select_paren_union_query_term ')' + st_select_lex_unit *unit; + if (!(unit= Lex->parsed_body_select($1, $3))) + MYSQL_YYABORT; + if (Lex->select_finalize(unit)) + MYSQL_YYABORT; + } + ; + + +simple_table: + query_specification { $$= $1; } + | table_value_constructor { $$= $1; } ; -select_paren_view: +table_value_constructor: + VALUES + { + if (Lex->parsed_TVC_start()) + MYSQL_YYABORT; + } + values_list + { + if (!($$= Lex->parsed_TVC_end())) + MYSQL_YYABORT; + } + ; + +query_specification_start: + SELECT_SYM { - /* - In order to correctly parse UNION's global ORDER BY we need to - set braces before parsing the clause. - */ - Lex->current_select->set_braces(true); + SELECT_LEX *sel; + LEX *lex= Lex; + if (!(sel= lex->alloc_select(TRUE)) || + lex->push_select(sel)) + MYSQL_YYABORT; + sel->init_select(); + sel->braces= FALSE; } - SELECT_SYM select_options_and_item_list select_part3_view - opt_select_lock_type + select_options { - DBUG_ASSERT(Lex->current_select->braces); + Select->parsing_place= SELECT_LIST; } - | '(' select_paren_view ')' - ; + select_item_list + { + Select->parsing_place= NO_MATTER; + } + ; -/* The equivalent of select_paren for nested queries. */ -select_paren_derived: +query_specification: + query_specification_start + opt_from_clause + opt_where_clause + opt_group_clause + opt_having_clause + opt_window_clause { - Lex->current_select->set_braces(true); + $$= Lex->pop_select(); } - table_value_constructor + ; + +select_into_query_specification: + query_specification_start + into + opt_from_clause + opt_where_clause + opt_group_clause + opt_having_clause + opt_window_clause { - DBUG_ASSERT(Lex->current_select->braces); - $$= Lex->current_select->master_unit()->first_select(); + $$= Lex->pop_select(); } - | + ; + +opt_from_clause: + /* Empty */ + | from_clause + ; + + +query_primary: + simple_table + { $$= $1; } + | query_primary_parens + { $$= $1; } + ; + +query_primary_parens: + '(' query_expression_unit { - Lex->current_select->set_braces(true); + if (Lex->parsed_unit_in_brackets($2)) + MYSQL_YYABORT; } - SELECT_SYM select_part2_derived - opt_table_expression - opt_order_clause - opt_limit_clause - opt_select_lock_type + query_expression_tail ')' { - DBUG_ASSERT(Lex->current_select->braces); - $$= Lex->current_select->master_unit()->first_select(); + $$= Lex->parsed_unit_in_brackets_tail($2, $4); } - | '(' select_paren_derived ')' { $$= $2; } - ; - -select_init3: - opt_table_expression - opt_select_lock_type + | '(' query_primary { - /* Parentheses carry no meaning here */ - Lex->current_select->set_braces(false); + Lex->push_select($2); } - union_clause - | select_part3_union_not_ready - opt_select_lock_type + query_expression_tail ')' { - /* Parentheses carry no meaning here */ - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_select_in_brackets($2, $4))) + YYABORT; } ; - -select_init3_union_query_term: - opt_table_expression - opt_select_lock_type +query_expression_unit: + query_primary + unit_type_decl + query_primary { - /* Parentheses carry no meaning here */ - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_select_expr_start($1, $3, $2.unit_type, + $2.distinct))) + YYABORT; } - union_clause - | select_part3_union_not_ready_noproc - opt_select_lock_type + | query_expression_unit + unit_type_decl + query_primary { - /* Parentheses carry no meaning here */ - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_select_expr_cont($1, $3, $2.unit_type, + $2.distinct, FALSE))) + YYABORT; } ; - -select_init3_view: - opt_table_expression opt_select_lock_type +query_expression_body: + query_primary { - Lex->current_select->set_braces(false); + Lex->push_select($1); } - | opt_table_expression opt_select_lock_type + query_expression_tail { - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_body_select($1, $3))) + MYSQL_YYABORT; } - union_list_view - | order_or_limit opt_select_lock_type + | query_expression_unit { - Lex->current_select->set_braces(false); + if (Lex->parsed_body_unit($1)) + MYSQL_YYABORT; } - | table_expression order_or_limit opt_select_lock_type + query_expression_tail { - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_body_unit_tail($1, $3))) + MYSQL_YYABORT; } ; -/* - The SELECT parts after select_item_list that cannot be followed by UNION. -*/ - -select_part3: - opt_table_expression - | select_part3_union_not_ready - ; - -select_part3_union_query_term: - opt_table_expression - | select_part3_union_not_ready_noproc - ; - -select_part3_view: - opt_table_expression - | order_or_limit - | table_expression order_or_limit - ; - -select_part3_union_not_ready: - select_part3_union_not_ready_noproc - | table_expression procedure_clause - | table_expression order_or_limit procedure_clause +query_expression: + opt_with_clause + query_expression_body + { + if ($1) + { + $2->set_with_clause($1); + $1->attach_to($2->first_select()); + } + $$= $2; + } ; -select_part3_union_not_ready_noproc: - order_or_limit - | into opt_table_expression opt_order_clause opt_limit_clause - | table_expression into - | table_expression order_or_limit - | table_expression order_or_limit into - ; -select_options_and_item_list: - { - LEX *lex= Lex; - SELECT_LEX *sel= lex->current_select; - if (sel->linkage != UNION_TYPE) - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; - } - select_options select_item_list +subselect: + remember_tok_start + query_expression { - Select->parsing_place= NO_MATTER; + if (!($$= Lex->parsed_subselect($2, $1))) + YYABORT; } ; @@ -9202,18 +9227,6 @@ select_options_and_item_list: /** <table expression>, as in the SQL standard. */ -table_expression: - from_clause - opt_where_clause - opt_group_clause - opt_having_clause - opt_window_clause - ; - -opt_table_expression: - /* Empty */ - | table_expression - ; from_clause: FROM table_reference_list @@ -9262,8 +9275,9 @@ history_point: TIMESTAMP TEXT_STRING { Item *item; - if (!(item= create_temporal_literal(thd, $2.str, $2.length, YYCSCL, - MYSQL_TYPE_DATETIME, true))) + if (!(item= type_handler_datetime.create_literal_item(thd, + $2.str, $2.length, + YYCSCL, true))) MYSQL_YYABORT; $$= Vers_history_point(VERS_TIMESTAMP, item); } @@ -9316,59 +9330,68 @@ select_option: query_expression_option | SQL_NO_CACHE_SYM { - /* - Allow this flag only on the first top-level SELECT statement, if - SQL_CACHE wasn't specified, and only once per query. - */ - if (unlikely(Lex->current_select != &Lex->select_lex)) - my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE")); - if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE)) - my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE")); - if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE)) + /* + Allow this flag once per query. + */ + if (Select->options & OPTION_NO_QUERY_CACHE) my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE")); - - Lex->safe_to_cache_query=0; - Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE; - Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE; + Select->options|= OPTION_NO_QUERY_CACHE; } | SQL_CACHE_SYM { - /* - Allow this flag only on the first top-level SELECT statement, if - SQL_NO_CACHE wasn't specified, and only once per query. - */ - if (unlikely(Lex->current_select != &Lex->select_lex)) - my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE")); - if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE)) - my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE")); - if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE)) + /* + Allow this flag once per query. + */ + if (Select->options & OPTION_TO_QUERY_CACHE) my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE")); - - Lex->safe_to_cache_query=1; - Lex->select_lex.options|= OPTION_TO_QUERY_CACHE; - Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE; + Select->options|= OPTION_TO_QUERY_CACHE; } ; -opt_select_lock_type: - /* empty */ - | FOR_SYM UPDATE_SYM opt_lock_wait_timeout + +select_lock_type: + FOR_SYM UPDATE_SYM opt_lock_wait_timeout_new { - LEX *lex=Lex; - lex->current_select->lock_type= TL_WRITE; - lex->current_select->set_lock_for_tables(TL_WRITE); - lex->safe_to_cache_query=0; + $$= $3; + $$.defined_lock= TRUE; + $$.update_lock= TRUE; } - | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout_new { - LEX *lex=Lex; - lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS; - lex->current_select-> - set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS); - lex->safe_to_cache_query=0; + $$= $5; + $$.defined_lock= TRUE; + $$.update_lock= FALSE; } ; +opt_select_lock_type: + /* empty */ + { + $$.empty(); + } + | select_lock_type + { + $$= $1; + } + ; + +opt_lock_wait_timeout_new: + /* empty */ + { + $$.empty(); + } + | WAIT_SYM ulong_num + { + $$.defined_timeout= TRUE; + $$.timeout= $2; + } + | NOWAIT_SYM + { + $$.defined_timeout= TRUE; + $$.timeout= 0; + } + ; + select_item_list: select_item_list ',' select_item | select_item @@ -10023,7 +10046,21 @@ column_default_non_parenthesized_expr: | param_marker { $$= $1; } | variable | sum_expr + { + if (!Lex->select_stack_top) + { + my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0)); + MYSQL_YYABORT; + } + } | window_func_expr + { + if (!Lex->select_stack_top) + { + my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0)); + MYSQL_YYABORT; + } + } | inverse_distribution_function | ROW_SYM '(' expr ',' expr_list ')' { @@ -10201,7 +10238,7 @@ function_call_keyword_timestamp: } | TIMESTAMP '(' expr ',' expr ')' { - $$= new (thd->mem_root) Item_func_add_time(thd, $3, $5, 1, 0); + $$= new (thd->mem_root) Item_func_timestamp(thd, $3, $5); if (unlikely($$ == NULL)) MYSQL_YYABORT; } @@ -11616,10 +11653,15 @@ esc_table_ref: /* Equivalent to <table reference list> in the SQL:2003 standard. */ /* Warning - may return NULL in case of incomplete SELECT */ derived_table_list: - esc_table_ref { $$=$1; } + esc_table_ref + { + $$=$1; + Select->add_joined_table($1); + } | derived_table_list ',' esc_table_ref { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); + Select->add_joined_table($3); } ; @@ -11638,11 +11680,18 @@ join_table: left-associative joins. */ table_ref normal_join table_ref %prec TABLE_REF_PRIORITY - { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=$2; } + { + MYSQL_YYABORT_UNLESS($1 && ($$=$3)); + Select->add_joined_table($1); + Select->add_joined_table($3); + $3->straight=$2; + } | table_ref normal_join table_ref ON { MYSQL_YYABORT_UNLESS($1 && $3); + Select->add_joined_table($1); + Select->add_joined_table($3); /* Change the current name resolution context to a local context. */ if (unlikely(push_new_name_resolution_context(thd, $1, $3))) MYSQL_YYABORT; @@ -11659,6 +11708,8 @@ join_table: USING { MYSQL_YYABORT_UNLESS($1 && $3); + Select->add_joined_table($1); + Select->add_joined_table($3); } '(' using_list ')' { @@ -11669,6 +11720,8 @@ join_table: | table_ref NATURAL inner_join table_factor { MYSQL_YYABORT_UNLESS($1 && ($$=$4)); + Select->add_joined_table($1); + Select->add_joined_table($4); $4->straight=$3; add_join_natural($1,$4,NULL,Select); } @@ -11678,6 +11731,8 @@ join_table: ON { MYSQL_YYABORT_UNLESS($1 && $5); + Select->add_joined_table($1); + Select->add_joined_table($5); /* Change the current name resolution context to a local context. */ if (unlikely(push_new_name_resolution_context(thd, $1, $5))) MYSQL_YYABORT; @@ -11694,6 +11749,8 @@ join_table: | table_ref LEFT opt_outer JOIN_SYM table_factor { MYSQL_YYABORT_UNLESS($1 && $5); + Select->add_joined_table($1); + Select->add_joined_table($5); } USING '(' using_list ')' { @@ -11704,6 +11761,8 @@ join_table: | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor { MYSQL_YYABORT_UNLESS($1 && $6); + Select->add_joined_table($1); + Select->add_joined_table($6); add_join_natural($1,$6,NULL,Select); $6->outer_join|=JOIN_TYPE_LEFT; $$=$6; @@ -11714,6 +11773,8 @@ join_table: ON { MYSQL_YYABORT_UNLESS($1 && $5); + Select->add_joined_table($1); + Select->add_joined_table($5); /* Change the current name resolution context to a local context. */ if (unlikely(push_new_name_resolution_context(thd, $1, $5))) MYSQL_YYABORT; @@ -11731,6 +11792,8 @@ join_table: | table_ref RIGHT opt_outer JOIN_SYM table_factor { MYSQL_YYABORT_UNLESS($1 && $5); + Select->add_joined_table($1); + Select->add_joined_table($5); } USING '(' using_list ')' { @@ -11742,6 +11805,8 @@ join_table: | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor { MYSQL_YYABORT_UNLESS($1 && $6); + Select->add_joined_table($1); + Select->add_joined_table($6); add_join_natural($6,$1,NULL,Select); LEX *lex= Lex; if (unlikely(!($$= lex->current_select->convert_right_join()))) @@ -11777,238 +11842,44 @@ use_partition: } ; -/* - This is a flattening of the rules <table factor> and <table primary> - in the SQL:2003 standard, since we don't have <sample clause> - - I.e. - <table factor> ::= <table primary> [ <sample clause> ] -*/ -/* Warning - may return NULL in case of incomplete SELECT */ table_factor: - table_primary_ident - | table_primary_derived + table_primary_ident { $$= $1; } + | table_primary_derived { $$= $1; } + | join_table_parens { $$= $1; } + | table_reference_list_parens { $$= $1; } ; -table_primary_ident: - { - DBUG_ASSERT(Select); - SELECT_LEX *sel= Select; - sel->table_join_options= 0; - } - table_ident opt_use_partition opt_for_system_time_clause opt_table_alias opt_key_definition - { - if (unlikely(!($$= Select->add_table_to_list(thd, $2, $5, - Select->get_table_join_options(), - YYPS->m_lock_type, - YYPS->m_mdl_type, - Select-> - pop_index_hints(), - $3)))) - MYSQL_YYABORT; - TABLE_LIST *tl= $$; - Select->add_joined_table(tl); - if ($4) - tl->vers_conditions= Lex->vers_conditions; - } - ; - - - -/* - Represents a flattening of the following rules from the SQL:2003 - standard. This sub-rule corresponds to the sub-rule - <table primary> ::= ... | <derived table> [ AS ] <correlation name> - - <derived table> ::= <table subquery> - <table subquery> ::= <subquery> - <subquery> ::= <left paren> <query expression> <right paren> - <query expression> ::= [ <with clause> ] <query expression body> - - For the time being we use the non-standard rule - select_derived_union which is a compromise between the standard - and our parser. Possibly this rule could be replaced by our - query_expression_body. -*/ - -table_primary_derived: - '(' get_select_lex select_derived_union ')' opt_for_system_time_clause opt_table_alias +table_reference_list_parens: + '(' table_reference_list_parens ')' { $$= $2; } + | '(' nested_table_reference_list ')' { - /* Use $2 instead of Lex->current_select as derived table will - alter value of Lex->current_select. */ - if (!($3 || $6) && $2->embedding && - !$2->embedding->nested_join->join_list.elements) - { - /* we have a derived table ($3 == NULL) but no alias, - Since we are nested in further parentheses so we - can pass NULL to the outer level parentheses - Permits parsing of "((((select ...))) as xyz)" */ - $$= 0; - } - else if (!$3) - { - /* Handle case of derived table, alias may be NULL if there - are no outer parentheses, add_table_to_list() will throw - error in this case */ - LEX *lex=Lex; - lex->check_automatic_up(UNSPECIFIED_TYPE); - SELECT_LEX *sel= lex->current_select; - SELECT_LEX_UNIT *unit= sel->master_unit(); - lex->current_select= sel= unit->outer_select(); - Table_ident *ti= new (thd->mem_root) Table_ident(unit); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!($$= sel->add_table_to_list(thd, - ti, $6, 0, - TL_READ, - MDL_SHARED_READ)))) - MYSQL_YYABORT; - sel->add_joined_table($$); - lex->pop_context(); - lex->nest_level--; - } - else if (unlikely($6 != NULL)) - { - /* - Tables with or without joins within parentheses cannot - have aliases, and we ruled out derived tables above. - */ - thd->parse_error(); + if (!($$= Select->end_nested_join(thd))) MYSQL_YYABORT; - } - else - { - /* nested join: FROM (t1 JOIN t2 ...), - nest_level is the same as in the outer query */ - $$= $3; - } - /* - Fields in derived table can be used in upper select in - case of merge. We do not add HAVING fields because we do - not merge such derived. We do not add union because - also do not merge them - */ - if ($$ && $$->derived && - !$$->derived->first_select()->next_select()) - $$->select_lex->add_where_field($$->derived->first_select()); - if ($5) - { - MYSQL_YYABORT_UNLESS(!$3); - $$->vers_conditions= Lex->vers_conditions; - } } - /* Represents derived table with WITH clause */ - | '(' get_select_lex subselect_start - with_clause query_expression_body - subselect_end ')' opt_for_system_time_clause opt_table_alias - { - LEX *lex=Lex; - SELECT_LEX *sel= $2; - SELECT_LEX_UNIT *unit= $5->master_unit(); - Table_ident *ti= new (thd->mem_root) Table_ident(unit); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - $5->set_with_clause($4); - lex->current_select= sel; - if (unlikely(!($$= sel->add_table_to_list(lex->thd, - ti, $9, 0, - TL_READ, - MDL_SHARED_READ)))) - MYSQL_YYABORT; - sel->add_joined_table($$); - if ($8) - $$->vers_conditions= Lex->vers_conditions; - } ; -/* - This rule accepts just about anything. The reason is that we have - empty-producing rules in the beginning of rules, in this case - subselect_start. This forces bison to take a decision which rules to - reduce by long before it has seen any tokens. This approach ties us - to a very limited class of parseable languages, and unfortunately - SQL is not one of them. The chosen 'solution' was this rule, which - produces just about anything, even complete bogus statements, for - instance ( table UNION SELECT 1 ). - Fortunately, we know that the semantic value returned by - select_derived is NULL if it contained a derived table, and a pointer to - the base table's TABLE_LIST if it was a base table. So in the rule - regarding union's, we throw a parse error manually and pretend it - was bison that did it. - - Also worth noting is that this rule concerns query expressions in - the from clause only. Top level select statements and other types of - subqueries have their own union rules. -*/ -select_derived_union: - select_derived - | select_derived union_order_or_limit +nested_table_reference_list: + table_ref ',' table_ref { - if (unlikely($1)) - { - thd->parse_error(); + if (Select->init_nested_join(thd)) MYSQL_YYABORT; - } - } - | select_derived union_head_non_top - { - if (unlikely($1)) - { - thd->parse_error(); - MYSQL_YYABORT; - } - } - union_list_derived_part2 - | derived_simple_table opt_select_lock_type - | derived_simple_table order_or_limit opt_select_lock_type - | derived_simple_table opt_select_lock_type union_list_derived - ; - -union_list_derived_part2: - query_term_union_not_ready { Lex->pop_context(); } - | query_term_union_ready { Lex->pop_context(); } - | query_term_union_ready { Lex->pop_context(); } union_list_derived - ; - -union_list_derived: - union_head_non_top union_list_derived_part2 - ; - - -/* The equivalent of select_init2 for nested queries. */ -select_init2_derived: - select_part2_derived - { - Select->set_braces(0); - } - ; - -/* The equivalent of select_part2 for nested queries. */ -select_part2_derived: - { - LEX *lex= Lex; - SELECT_LEX *sel= lex->current_select; - if (sel->linkage != UNION_TYPE) - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; + Select->add_joined_table($1); + Select->add_joined_table($3); + $$= $1->embedding; } - opt_query_expression_options select_item_list + | nested_table_reference_list ',' table_ref { - Select->parsing_place= NO_MATTER; + Select->add_joined_table($3); + $$= $1; } ; -/* handle contents of parentheses in join expression */ -select_derived: - get_select_lex_derived derived_table_list +join_table_parens: + '(' join_table_parens ')' { $$= $2; } + | '(' join_table ')' { LEX *lex= Lex; - /* for normal joins, $2 != NULL and end_nested_join() != NULL, - for derived tables, both must equal NULL */ - - if (unlikely(!($$= $1->end_nested_join(lex->thd)) && $2)) - MYSQL_YYABORT; - if (unlikely(!$2 && $$)) + if (!($$= lex->current_select->nest_last_join(thd))) { thd->parse_error(); MYSQL_YYABORT; @@ -12016,86 +11887,59 @@ select_derived: } ; -derived_simple_table: - derived_query_specification { $$= $1; } - | derived_table_value_constructor { $$= $1; } - ; -/* - Similar to query_specification, but for derived tables. - Example: the inner parenthesized SELECT in this query: - SELECT * FROM (SELECT * FROM t1); -*/ -derived_query_specification: - SELECT_SYM select_derived_init select_derived2 - { - if ($2) - Select->set_braces(1); - $$= NULL; - } - ; -derived_table_value_constructor: - VALUES - { - Lex->tvc_start(); - } - values_list +table_primary_ident: + table_ident opt_use_partition opt_for_system_time_clause + opt_table_alias_clause opt_key_definition { - if (Lex->tvc_finalize_derived()) + SELECT_LEX *sel= Select; + sel->table_join_options= 0; + if (!($$= Select->add_table_to_list(thd, $1, $4, + Select->get_table_join_options(), + YYPS->m_lock_type, + YYPS->m_mdl_type, + Select->pop_index_hints(), + $2))) MYSQL_YYABORT; - $$= NULL; + TABLE_LIST *tl= $$; + if ($3) + tl->vers_conditions= Lex->vers_conditions; } ; -select_derived2: - { - LEX *lex= Lex; - lex->derived_tables|= DERIVED_SUBQUERY; - if (unlikely(!lex->expr_allows_subselect || - lex->sql_command == (int)SQLCOM_PURGE)) - { - thd->parse_error(); - MYSQL_YYABORT; - } - if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || - unlikely(mysql_new_select(lex, 1, NULL))) - MYSQL_YYABORT; - mysql_init_select(lex); - lex->current_select->linkage= DERIVED_TABLE_TYPE; - lex->current_select->parsing_place= SELECT_LIST; - } - select_options select_item_list - { - Select->parsing_place= NO_MATTER; - } - opt_table_expression - ; +/* + Represents a flattening of the following rules from the SQL:2003 + standard. This sub-rule corresponds to the sub-rule + <table primary> ::= ... | <derived table> [ AS ] <correlation name> -get_select_lex: - /* Empty */ { $$= Select; } - ; + <derived table> ::= <table subquery> + <table subquery> ::= <subquery> + <subquery> ::= <left paren> <query expression> <right paren> + <query expression> ::= [ <with clause> ] <query expression body> -get_select_lex_derived: - get_select_lex + For the time being we use the non-standard rule + select_derived_union which is a compromise between the standard + and our parser. Possibly this rule could be replaced by our + query_expression_body. +*/ + +table_primary_derived: + query_primary_parens opt_for_system_time_clause table_alias_clause { - LEX *lex= Lex; - if (unlikely($1->init_nested_join(lex->thd))) - MYSQL_YYABORT; + if (!($$= Lex->parsed_derived_select($1, $2, $3))) + YYABORT; } - ; - -select_derived_init: + | '(' + query_expression + ')' opt_for_system_time_clause table_alias_clause { - LEX *lex= Lex; - - TABLE_LIST *embedding= lex->current_select->embedding; - $$= embedding && - !embedding->nested_join->join_list.elements; - /* return true if we are deeply nested */ + if (!($$= Lex->parsed_derived_unit($2, $4, $5))) + YYABORT; } ; + opt_outer: /* empty */ {} | OUTER {} @@ -12226,9 +12070,14 @@ table_alias: | '=' ; -opt_table_alias: +opt_table_alias_clause: /* empty */ { $$=0; } - | table_alias ident_table_alias + + | table_alias_clause { $$= $1; } + ; + +table_alias_clause: + table_alias ident_table_alias { $$= (LEX_CSTRING*) thd->memdup(&$2,sizeof(LEX_STRING)); if (unlikely($$ == NULL)) @@ -12324,7 +12173,7 @@ olap_opt: SQL-2003: GROUP BY ... CUBE(col1, col2, col3) */ LEX *lex=Lex; - if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)) + if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE)) my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH CUBE", "global union parameters")); lex->current_select->olap= CUBE_TYPE; @@ -12341,7 +12190,7 @@ olap_opt: SQL-2003: GROUP BY ... ROLLUP(col1, col2, col3) */ LEX *lex= Lex; - if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)) + if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE)) my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", "global union parameters")); lex->current_select->olap= ROLLUP_TYPE; @@ -12401,7 +12250,7 @@ opt_window_partition_clause: opt_window_order_clause: /* empty */ { } - | ORDER_SYM BY order_list + | ORDER_SYM BY order_list { Select->order_list= *($3); } ; opt_window_frame_clause: @@ -12525,70 +12374,35 @@ alter_order_item: opt_order_clause: /* empty */ + { $$= NULL; } | order_clause + { $$= $1; } ; order_clause: ORDER_SYM BY { - LEX *lex=Lex; - SELECT_LEX *sel= lex->current_select; - SELECT_LEX_UNIT *unit= sel-> master_unit(); - if (unlikely(sel->linkage != GLOBAL_OPTIONS_TYPE && - sel->olap != UNSPECIFIED_OLAP_TYPE && - (sel->linkage != UNION_TYPE || sel->braces))) - { - my_error(ER_WRONG_USAGE, MYF(0), - "CUBE/ROLLUP", "ORDER BY"); - MYSQL_YYABORT; - } - if (lex->sql_command != SQLCOM_ALTER_TABLE && - !unit->fake_select_lex) - { - /* - A query of the of the form (SELECT ...) ORDER BY order_list is - executed in the same way as the query - SELECT ... ORDER BY order_list - unless the SELECT construct contains ORDER BY or LIMIT clauses. - Otherwise we create a fake SELECT_LEX if it has not been - created yet. - */ - SELECT_LEX *first_sl= unit->first_select(); - if (unlikely(!unit->is_unit_op() && - (first_sl->order_list.elements || - first_sl->select_limit) && - unit->add_fake_select_lex(thd))) - MYSQL_YYABORT; - } - if (sel->master_unit()->is_unit_op() && !sel->braces) - { - /* - At this point we don't know yet whether this is the last - select in union or not, but we move ORDER BY to - fake_select_lex anyway. If there would be one more select - in union mysql_new_select will correctly throw error. - */ - DBUG_ASSERT(sel->master_unit()->fake_select_lex); - lex->current_select= sel->master_unit()->fake_select_lex; - } + thd->where= "ORDER clause"; } order_list { - + $$= $4; } ; order_list: order_list ',' order_ident order_dir { - if (unlikely(add_order_to_list(thd, $3,(bool) $4))) - MYSQL_YYABORT; - } + $$= $1; + if (add_to_list(thd, *$$, $3,(bool) $4)) + MYSQL_YYABORT; + } | order_ident order_dir { - if (unlikely(add_order_to_list(thd, $1,(bool) $2))) + $$= new (thd->mem_root) SQL_I_List<ORDER>(); + if (add_to_list(thd, *$$, $1, (bool) $2)) MYSQL_YYABORT; - } + } ; order_dir: @@ -12598,63 +12412,61 @@ order_dir: ; opt_limit_clause: - /* empty */ {} - | limit_clause {} + /* empty */ + { $$.empty(); } + | limit_clause + { $$= $1; } ; -limit_clause_init: - LIMIT - { - SELECT_LEX *sel= Select; - if (sel->master_unit()->is_unit_op() && !sel->braces) - { - /* Move LIMIT that belongs to UNION to fake_select_lex */ - Lex->current_select= sel->master_unit()->fake_select_lex; - DBUG_ASSERT(Select); - } - } - ; - limit_clause: - limit_clause_init limit_options + LIMIT limit_options { - SELECT_LEX *sel= Select; - if (!sel->select_limit->basic_const_item() || - sel->select_limit->val_int() > 0) + $$= $2; + if (!$$.select_limit->basic_const_item() || + $$.select_limit->val_int() > 0) Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); } - | limit_clause_init limit_options + | LIMIT limit_options ROWS_SYM EXAMINED_SYM limit_rows_option { + $$= $2; Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); } - | limit_clause_init ROWS_SYM EXAMINED_SYM limit_rows_option + | LIMIT ROWS_SYM EXAMINED_SYM limit_rows_option { + $$.select_limit= 0; + $$.offset_limit= 0; + $$.explicit_limit= 1; Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); } ; +opt_global_limit_clause: + opt_limit_clause + { + Select->explicit_limit= $1.explicit_limit; + Select->select_limit= $1.select_limit; + Select->offset_limit= $1.offset_limit; + } + limit_options: limit_option { - SELECT_LEX *sel= Select; - sel->select_limit= $1; - sel->offset_limit= 0; - sel->explicit_limit= 1; + $$.select_limit= $1; + $$.offset_limit= 0; + $$.explicit_limit= 1; } | limit_option ',' limit_option { - SELECT_LEX *sel= Select; - sel->select_limit= $3; - sel->offset_limit= $1; - sel->explicit_limit= 1; + $$.select_limit= $3; + $$.offset_limit= $1; + $$.explicit_limit= 1; } | limit_option OFFSET_SYM limit_option { - SELECT_LEX *sel= Select; - sel->select_limit= $1; - sel->offset_limit= $3; - sel->explicit_limit= 1; + $$.select_limit= $1; + $$.offset_limit= $3; + $$.explicit_limit= 1; } ; @@ -12717,6 +12529,77 @@ delete_limit_clause: | LIMIT limit_option ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; } ; +opt_order_limit_lock: + /* empty */ + { $$= NULL; } + | order_or_limit + { + $$= $1; + $$->lock.empty(); + } + | order_or_limit select_lock_type + { + $$= $1; + $$->lock= $2; + } + | select_lock_type + { + $$= new(thd->mem_root) Lex_order_limit_lock; + if (!$$) + YYABORT; + $$->order_list= NULL; + $$->limit.empty(); + $$->lock= $1; + } + ; +query_expression_tail: + opt_order_limit_lock + ; + +opt_procedure_or_into: + /* empty */ + { + $$.empty(); + } + | procedure_clause opt_select_lock_type + { + $$= $2; + } + | into opt_select_lock_type + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_DEPRECATED_SYNTAX, + ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX), + "<select expression> INTO <destination>;", + "'SELECT <select list> INTO <destination>" + " FROM...'"); + $$= $2; + } + ; + + +order_or_limit: + order_clause opt_limit_clause + { + $$= new(thd->mem_root) Lex_order_limit_lock; + if (!$$) + YYABORT; + $$->order_list= $1; + $$->limit= $2; + } + | limit_clause + { + Lex_order_limit_lock *op= $$= new(thd->mem_root) Lex_order_limit_lock; + if (!$$) + YYABORT; + op->order_list= NULL; + op->limit= $1; + $$->order_list= NULL; + $$->limit= $1; + } + ; + + opt_plus: /* empty */ | '+' @@ -12786,14 +12669,11 @@ bool: | TRUE_SYM { $$= 1; } | FALSE_SYM { $$= 0; } - procedure_clause: PROCEDURE_SYM ident /* Procedure name */ { LEX *lex=Lex; - DBUG_ASSERT(&lex->select_lex == lex->current_select); - lex->proc_list.elements=0; lex->proc_list.first=0; lex->proc_list.next= &lex->proc_list.first; @@ -12813,6 +12693,7 @@ procedure_clause: parameters are reduced. */ Lex->expr_allows_subselect= false; + Select->options|= OPTION_PROCEDURE_CLAUSE; } '(' procedure_list ')' { @@ -12896,6 +12777,7 @@ select_outvar: into: INTO into_destination + {} ; into_destination: @@ -13089,10 +12971,11 @@ table_list: table_name: table_ident { - if (unlikely(!Select->add_table_to_list(thd, $1, NULL, - TL_OPTION_UPDATING, - YYPS->m_lock_type, - YYPS->m_mdl_type))) + if (!thd->lex->current_select_or_default()-> + add_table_to_list(thd, $1, NULL, + TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type)) MYSQL_YYABORT; } ; @@ -13166,16 +13049,23 @@ insert: LEX *lex= Lex; lex->sql_command= SQLCOM_INSERT; lex->duplicates= DUP_ERROR; + if (Lex->main_select_push()) + MYSQL_YYABORT; mysql_init_select(lex); + lex->current_select->parsing_place= BEFORE_OPT_LIST; } insert_lock_option opt_ignore insert2 { Select->set_lock_for_tables($3); - Lex->current_select= &Lex->select_lex; + Lex->current_select= Lex->first_select_lex(); } insert_field_spec opt_insert_update - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } ; replace: @@ -13184,15 +13074,22 @@ replace: LEX *lex=Lex; lex->sql_command = SQLCOM_REPLACE; lex->duplicates= DUP_REPLACE; + if (Lex->main_select_push()) + MYSQL_YYABORT; mysql_init_select(lex); + lex->current_select->parsing_place= BEFORE_OPT_LIST; } replace_lock_option insert2 { Select->set_lock_for_tables($3); - Lex->current_select= &Lex->select_lex; + Lex->current_select= Lex->first_select_lex(); } insert_field_spec - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } ; insert_lock_option: @@ -13235,15 +13132,14 @@ insert_table: table_name_with_opt_use_partition { LEX *lex=Lex; - lex->field_list.empty(); + //lex->field_list.empty(); lex->many_values.empty(); lex->insert_list=0; }; insert_field_spec: insert_values {} - | '(' ')' insert_values {} - | '(' fields ')' insert_values {} + | insert_field_list insert_values {} | SET { LEX *lex=Lex; @@ -13251,20 +13147,33 @@ insert_field_spec: unlikely(lex->many_values.push_back(lex->insert_list, thd->mem_root))) MYSQL_YYABORT; + lex->current_select->parsing_place= NO_MATTER; } ident_eq_list ; +insert_field_list: + LEFT_PAREN_ALT opt_fields ')' + { + Lex->current_select->parsing_place= AFTER_LIST; + } + ; + +opt_fields: + /* empty */ + | fields + ; + fields: fields ',' insert_ident { Lex->field_list.push_back($3, thd->mem_root); } | insert_ident { Lex->field_list.push_back($1, thd->mem_root); } ; + + insert_values: - VALUES values_list {} - | VALUE_SYM values_list {} - | create_select_query_expression {} + create_select_query_expression {} ; values_list: @@ -13414,6 +13323,8 @@ update: UPDATE_SYM { LEX *lex= Lex; + if (Lex->main_select_push()) + MYSQL_YYABORT; mysql_init_select(lex); lex->sql_command= SQLCOM_UPDATE; lex->duplicates= DUP_ERROR; @@ -13422,13 +13333,14 @@ update: SET update_list { LEX *lex= Lex; - if (lex->select_lex.table_list.elements > 1) + if (lex->first_select_lex()->table_list.elements > 1) lex->sql_command= SQLCOM_UPDATE_MULTI; - else if (unlikely(lex->select_lex.get_table_list()->derived)) + else if (lex->first_select_lex()->get_table_list()->derived) { /* it is single table update and it is update of derived table */ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - lex->select_lex.get_table_list()->alias.str, "UPDATE"); + lex->first_select_lex()->get_table_list()->alias.str, + "UPDATE"); MYSQL_YYABORT; } /* @@ -13438,7 +13350,14 @@ update: */ Select->set_lock_for_tables($3); } - opt_where_clause opt_order_clause delete_limit_clause {} + opt_where_clause opt_order_clause delete_limit_clause + { + if ($10) + Select->order_list= *($10); + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } ; update_list: @@ -13485,9 +13404,11 @@ delete: mysql_init_select(lex); YYPS->m_lock_type= TL_WRITE_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_WRITE; + if (Lex->main_select_push()) + MYSQL_YYABORT; lex->ignore= 0; - lex->select_lex.init_order(); + lex->first_select_lex()->order_list.empty(); } delete_part2 ; @@ -13508,6 +13429,7 @@ delete_part2: | HISTORY_SYM delete_single_table opt_delete_system_time { Lex->last_table()->vers_conditions= Lex->vers_conditions; + Lex->pop_select(); //main select } ; @@ -13531,7 +13453,12 @@ single_multi: opt_where_clause opt_order_clause delete_limit_clause - opt_select_expressions {} + opt_select_expressions + { + if ($3) + Select->order_list= *($3); + Lex->pop_select(); //main select + } | table_wild_list { mysql_init_multi_delete(Lex); @@ -13542,6 +13469,9 @@ single_multi: { if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex))) MYSQL_YYABORT; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } | FROM table_alias_ref_list { @@ -13553,6 +13483,9 @@ single_multi: { if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex))) MYSQL_YYABORT; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } ; @@ -13621,9 +13554,9 @@ truncate: LEX* lex= Lex; lex->sql_command= SQLCOM_TRUNCATE; lex->alter_info.reset(); - lex->select_lex.options= 0; - lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; - lex->select_lex.init_order(); + lex->first_select_lex()->options= 0; + lex->sql_cache= LEX::SQL_CACHE_UNSPECIFIED; + lex->first_select_lex()->order_list.empty(); YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; } @@ -13708,6 +13641,8 @@ show: LEX *lex=Lex; lex->wild=0; lex->ident= null_clex_str; + if (Lex->main_select_push()) + MYSQL_YYABORT; mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LIST; lex->create_info.init(); @@ -13715,6 +13650,7 @@ show: show_param { Select->parsing_place= NO_MATTER; + Lex->pop_select(); //main select } ; @@ -13730,40 +13666,40 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLES; - lex->select_lex.db= $3; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES))) + lex->first_select_lex()->db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)) MYSQL_YYABORT; } | opt_full TRIGGERS_SYM opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TRIGGERS; - lex->select_lex.db= $3; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TRIGGERS))) + lex->first_select_lex()->db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)) MYSQL_YYABORT; } | EVENTS_SYM opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_EVENTS; - lex->select_lex.db= $2; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_EVENTS))) + lex->first_select_lex()->db= $2; + if (prepare_schema_table(thd, lex, 0, SCH_EVENTS)) MYSQL_YYABORT; } | TABLE_SYM STATUS_SYM opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; - lex->select_lex.db= $3; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLES))) + lex->first_select_lex()->db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_TABLES)) MYSQL_YYABORT; } | OPEN_SYM TABLES opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; - lex->select_lex.db= $3; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES))) + lex->first_select_lex()->db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)) MYSQL_YYABORT; } | PLUGINS_SYM @@ -13812,12 +13748,13 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS; } - opt_limit_clause + opt_global_limit_clause | RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS; - } opt_limit_clause + } + opt_global_limit_clause | keys_or_index from_or_in table_ident opt_db opt_where_clause { LEX *lex= Lex; @@ -13859,13 +13796,13 @@ show_param: LEX_CSTRING var= {STRING_WITH_LEN("error_count")}; (void) create_select_for_variable(thd, &var); } - | WARNINGS opt_limit_clause + | WARNINGS opt_global_limit_clause { Lex->sql_command = SQLCOM_SHOW_WARNS;} - | ERRORS opt_limit_clause + | ERRORS opt_global_limit_clause { Lex->sql_command = SQLCOM_SHOW_ERRORS;} | PROFILES_SYM { Lex->sql_command = SQLCOM_SHOW_PROFILES; } - | PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause + | PROFILE_SYM opt_profile_defs opt_profile_args opt_global_limit_clause { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_PROFILE; @@ -13927,7 +13864,7 @@ show_param: { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL,0))) + if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL,0)) MYSQL_YYABORT; lex->create_info.storage_media= HA_SM_DEFAULT; } @@ -13935,7 +13872,7 @@ show_param: { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL, 0))) + if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0)) MYSQL_YYABORT; lex->table_type= TABLE_TYPE_VIEW; } @@ -13943,7 +13880,7 @@ show_param: { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL, 0))) + if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0)) MYSQL_YYABORT; lex->table_type= TABLE_TYPE_SEQUENCE; } @@ -14160,7 +14097,7 @@ describe: mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LIST; lex->sql_command= SQLCOM_SHOW_FIELDS; - lex->select_lex.db= null_clex_str; + lex->first_select_lex()->db= null_clex_str; lex->verbose= 0; if (unlikely(prepare_schema_table(thd, lex, $2, SCH_COLUMNS))) MYSQL_YYABORT; @@ -14174,12 +14111,13 @@ describe: explainable_command { LEX *lex=Lex; - lex->select_lex.options|= SELECT_DESCRIBE; + lex->first_select_lex()->options|= SELECT_DESCRIBE; } ; explainable_command: select + | select_into | insert | replace | update @@ -14200,6 +14138,8 @@ analyze_stmt_command: opt_extended_describe: EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; } + | EXTENDED_SYM ALL + { Lex->describe|= DESCRIBE_EXTENDED | DESCRIBE_EXTENDED2; } | PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; } | opt_format_json {} ; @@ -14242,8 +14182,7 @@ flush: lex->type= 0; lex->no_write_to_binlog= $2; } - flush_options - {} + flush_options {} ; flush_options: @@ -14260,6 +14199,7 @@ flush_options: opt_table_list opt_flush_lock {} | flush_options_list + {} ; opt_flush_lock: @@ -14464,6 +14404,8 @@ purge_option: lex->value_list.empty(); lex->value_list.push_front($2, thd->mem_root); lex->sql_command= SQLCOM_PURGE_BEFORE; + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } ; @@ -14524,7 +14466,7 @@ use: { LEX *lex=Lex; lex->sql_command=SQLCOM_CHANGE_DB; - lex->select_lex.db= $2; + lex->first_select_lex()->db= $2; } ; @@ -14541,6 +14483,9 @@ load: $2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML"); MYSQL_YYABORT; } + if (lex->main_select_push()) + MYSQL_YYABORT; + mysql_init_select(lex); } load_data_lock opt_local INFILE TEXT_STRING_filesystem { @@ -14570,7 +14515,11 @@ load: opt_xml_rows_identified_by opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec opt_load_data_set_spec - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } ; data_or_xml: @@ -14768,11 +14717,6 @@ hex_or_bin_String: $1.length); if (unlikely(tmp == NULL)) MYSQL_YYABORT; - /* - it is OK only emulate fix_fields, because we need only - value of constant - */ - tmp->quick_fix_field(); $$= tmp->val_str((String*) 0); } | HEX_STRING @@ -14781,7 +14725,6 @@ hex_or_bin_String: $1.length); if (unlikely(tmp == NULL)) MYSQL_YYABORT; - tmp->quick_fix_field(); $$= tmp->val_str((String*) 0); } | BIN_NUM @@ -14794,7 +14737,6 @@ hex_or_bin_String: it is OK only emulate fix_fields, because we need only value of constant */ - tmp->quick_fix_field(); $$= tmp->val_str((String*) 0); } ; @@ -14930,26 +14872,23 @@ NUM_literal: temporal_literal: DATE_SYM TEXT_STRING { - if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length, - YYCSCL, - MYSQL_TYPE_DATE, - true)))) + if (unlikely(!($$= type_handler_newdate.create_literal_item(thd, + $2.str, $2.length, + YYCSCL, true)))) MYSQL_YYABORT; } | TIME_SYM TEXT_STRING { - if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length, - YYCSCL, - MYSQL_TYPE_TIME, - true)))) + if (unlikely(!($$= type_handler_time2.create_literal_item(thd, + $2.str, $2.length, + YYCSCL, true)))) MYSQL_YYABORT; } | TIMESTAMP TEXT_STRING { - if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length, - YYCSCL, - MYSQL_TYPE_DATETIME, - true)))) + if (unlikely(!($$= type_handler_datetime.create_literal_item(thd, + $2.str, $2.length, + YYCSCL, true)))) MYSQL_YYABORT; } ; @@ -14965,17 +14904,21 @@ opt_with_clause: with_clause: - WITH opt_recursive + WITH opt_recursive { + LEX *lex= Lex; With_clause *with_clause= new With_clause($2, Lex->curr_with_clause); if (unlikely(with_clause == NULL)) MYSQL_YYABORT; - Lex->derived_tables|= DERIVED_WITH; - Lex->curr_with_clause= with_clause; + lex->derived_tables|= DERIVED_WITH; + lex->curr_with_clause= with_clause; with_clause->add_to_list(Lex->with_clauses_list_last_next); + if (lex->current_select && + lex->current_select->parsing_place == BEFORE_OPT_LIST) + lex->current_select->parsing_place= NO_MATTER; } - with_list + with_list { $$= Lex->curr_with_clause; Lex->curr_with_clause= Lex->curr_with_clause->pop(); @@ -15004,15 +14947,14 @@ with_list_element: MYSQL_YYABORT; Lex->with_column_list.empty(); } - AS '(' remember_tok_start subselect remember_tok_end ')' + AS '(' remember_tok_start query_expression remember_tok_end ')' { LEX *lex= thd->lex; const char *query_start= lex->sphead ? lex->sphead->m_tmp_query : thd->query(); char *spec_start= $6 + 1; - With_element *elem= new With_element($1, *$2, $7->master_unit()); - if (unlikely(elem == NULL) || - unlikely(Lex->curr_with_clause->add_with_element(elem))) + With_element *elem= new With_element($1, *$2, $7); + if (elem == NULL || Lex->curr_with_clause->add_with_element(elem)) MYSQL_YYABORT; if (elem->set_unparsed_spec(thd, spec_start, $8, spec_start - query_start)) @@ -15952,14 +15894,22 @@ set: SET { LEX *lex=Lex; + if (lex->main_select_push()) + MYSQL_YYABORT; lex->set_stmt_init(); lex->var_list.empty(); sp_create_assignment_lex(thd, yychar == YYEMPTY); } start_option_value_list - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } | SET STATEMENT_SYM { + if (Lex->main_select_push()) + MYSQL_YYABORT; Lex->set_stmt_init(); } set_stmt_option_value_following_option_type_list @@ -15969,6 +15919,9 @@ set: my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "SET STATEMENT")); lex->stmt_var_list= lex->var_list; lex->var_list.empty(); + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } FOR_SYM verb_clause {} @@ -16310,14 +16263,14 @@ opt_for_user: ; text_or_password: - TEXT_STRING { Lex->definer->pwhash= $1;} + TEXT_STRING { Lex->definer->auth= $1;} | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; } | OLD_PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; - Lex->definer->pwhash.str= Item_func_password::alloc(thd, + Lex->definer->auth.str= Item_func_password::alloc(thd, $3.str, $3.length, Item_func_password::OLD); - Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + Lex->definer->auth.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; } ; @@ -16387,7 +16340,7 @@ table_lock_list: ; table_lock: - table_ident opt_table_alias lock_option + table_ident opt_table_alias_clause lock_option { thr_lock_type lock_type= (thr_lock_type) $3; bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE); @@ -16432,27 +16385,36 @@ unlock: */ handler: - HANDLER_SYM table_ident OPEN_SYM opt_table_alias + HANDLER_SYM + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + handler_tail + { + Lex->pop_select(); //main select + } + +handler_tail: + table_ident OPEN_SYM opt_table_alias_clause { LEX *lex= Lex; if (unlikely(lex->sphead)) my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); lex->sql_command = SQLCOM_HA_OPEN; - if (unlikely(!lex->current_select->add_table_to_list(thd, $2, $4, - 0))) + if (!lex->current_select->add_table_to_list(thd, $1, $3, 0)) MYSQL_YYABORT; } - | HANDLER_SYM table_ident_nodb CLOSE_SYM + | table_ident_nodb CLOSE_SYM { LEX *lex= Lex; if (unlikely(lex->sphead)) my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); lex->sql_command = SQLCOM_HA_CLOSE; - if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0, - 0))) + if (!lex->current_select->add_table_to_list(thd, $1, 0, 0)) MYSQL_YYABORT; } - | HANDLER_SYM table_ident_nodb READ_SYM + | table_ident_nodb READ_SYM { LEX *lex=Lex; if (unlikely(lex->sphead)) @@ -16466,15 +16428,24 @@ handler: lex->current_select->select_limit= one; lex->current_select->offset_limit= 0; lex->limit_rows_examined= 0; - if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0, - 0))) + if (!lex->current_select->add_table_to_list(thd, $1, 0, 0)) MYSQL_YYABORT; } - handler_read_or_scan opt_where_clause opt_limit_clause + handler_read_or_scan opt_where_clause opt_global_limit_clause { - Lex->expr_allows_subselect= TRUE; + LEX *lex=Lex; + lex->expr_allows_subselect= TRUE; + if (!lex->current_select->explicit_limit) + { + Item *one= new (thd->mem_root) Item_int(thd, (int32) 1); + if (one == NULL) + MYSQL_YYABORT; + lex->current_select->select_limit= one; + lex->current_select->offset_limit= 0; + lex->limit_rows_examined= 0; + } /* Stored functions are not supported for HANDLER READ. */ - if (unlikely(Lex->uses_stored_routines())) + if (lex->uses_stored_routines()) { my_error(ER_NOT_SUPPORTED_YET, MYF(0), "stored functions in HANDLER ... READ"); @@ -16857,13 +16828,11 @@ grant_user: { $$= $1; $1->pwtext= $4; - if (unlikely(Lex->sql_command == SQLCOM_REVOKE)) - MYSQL_YYABORT; } | user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING { $$= $1; - $1->pwhash= $5; + $1->auth= $5; } | user IDENTIFIED_SYM via_or_with ident_or_text { @@ -16871,12 +16840,20 @@ grant_user: $1->plugin= $4; $1->auth= empty_clex_str; } - | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as + TEXT_STRING_sys { $$= $1; $1->plugin= $4; $1->auth= $6; } + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as + PASSWORD_SYM '(' TEXT_STRING ')' + { + $$= $1; + $1->plugin= $4; + $1->pwtext= $8; + } | user_or_role { $$= $1; } ; @@ -17110,212 +17087,27 @@ release: */ unit_type_decl: - UNION_SYM - { $$= UNION_TYPE; } + UNION_SYM union_option + { $$.unit_type= UNION_TYPE; $$.distinct= $2; } | INTERSECT_SYM - { $$= INTERSECT_TYPE; } + { $$.unit_type= INTERSECT_TYPE; $$.distinct= 1; } | EXCEPT_SYM - { $$= EXCEPT_TYPE; } - - -union_clause: - /* empty */ {} - | union_list - ; - -union_list: - unit_type_decl union_option - { - if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE))) - MYSQL_YYABORT; - } - union_list_part2 - { - /* - Remove from the name resolution context stack the context of the - last select in the union. - */ - Lex->pop_context(); - } - ; - -union_list_view: - unit_type_decl union_option - { - if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE))) - MYSQL_YYABORT; - } - query_expression_body_view - { - Lex->pop_context(); - } - ; - -union_order_or_limit: - { - LEX *lex= thd->lex; - DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE); - SELECT_LEX *sel= lex->current_select; - SELECT_LEX_UNIT *unit= sel->master_unit(); - SELECT_LEX *fake= unit->fake_select_lex; - if (fake) - { - fake->no_table_names_allowed= 1; - lex->current_select= fake; - } - thd->where= "global ORDER clause"; - } - order_or_limit - { - thd->lex->current_select->no_table_names_allowed= 0; - thd->where= ""; - } - ; + { $$.unit_type= EXCEPT_TYPE; $$.distinct= 1; } -order_or_limit: - order_clause opt_limit_clause - | limit_clause - ; /* Start a UNION, for non-top level query expressions. */ -union_head_non_top: - unit_type_decl union_option - { - if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, FALSE))) - MYSQL_YYABORT; - } - ; - union_option: /* empty */ { $$=1; } | DISTINCT { $$=1; } | ALL { $$=0; } ; -simple_table: - query_specification { $$= $1; } - | table_value_constructor { $$= $1; } - ; - -table_value_constructor: - VALUES - { - Lex->tvc_start(); - } - values_list - { - $$= Lex->current_select; - if (Lex->tvc_finalize()) - MYSQL_YYABORT; - } - ; - -/* - Corresponds to the SQL Standard - <query specification> ::= - SELECT [ <set quantifier> ] <select list> <table expression> - - Notes: - - We allow more options in addition to <set quantifier> - - <table expression> is optional in MariaDB -*/ -query_specification: - SELECT_SYM select_init2_derived opt_table_expression - { - $$= Lex->current_select->master_unit()->first_select(); - } - ; - -query_term_union_not_ready: - simple_table order_or_limit opt_select_lock_type { $$= $1; } - | '(' select_paren_derived ')' union_order_or_limit { $$= $2; } - ; - -query_term_union_ready: - simple_table opt_select_lock_type { $$= $1; } - | '(' select_paren_derived ')' { $$= $2; } - ; - -query_expression_body: - query_term_union_not_ready { $$= $1; } - | query_term_union_ready { $$= $1; } - | query_term_union_ready union_list_derived { $$= $1; } - ; - -/* Corresponds to <query expression> in the SQL:2003 standard. */ -subselect: - subselect_start opt_with_clause query_expression_body subselect_end - { - $3->set_with_clause($2); - $$= $3; - } - ; - -subselect_start: - { - LEX *lex=Lex; - if (unlikely(!lex->expr_allows_subselect || - lex->sql_command == (int)SQLCOM_PURGE)) - { - thd->parse_error(); - MYSQL_YYABORT; - } - /* - we are making a "derived table" for the parenthesis - as we need to have a lex level to fit the union - after the parenthesis, e.g. - (SELECT .. ) UNION ... becomes - SELECT * FROM ((SELECT ...) UNION ...) - */ - if (unlikely(mysql_new_select(Lex, 1, NULL))) - MYSQL_YYABORT; - } - ; - -subselect_end: - { - LEX *lex=Lex; - - lex->check_automatic_up(UNSPECIFIED_TYPE); - lex->pop_context(); - SELECT_LEX *child= lex->current_select; - lex->current_select = lex->current_select->return_after_parsing(); - lex->nest_level--; - lex->current_select->n_child_sum_items += child->n_sum_items; - /* - A subselect can add fields to an outer select. Reserve space for - them. - */ - lex->current_select->select_n_where_fields+= - child->select_n_where_fields; - - /* - Aggregate functions in having clause may add fields to an outer - select. Count them also. - */ - lex->current_select->select_n_having_items+= - child->select_n_having_items; - } - ; - -opt_query_expression_options: - /* empty */ - | query_expression_option_list - ; - -query_expression_option_list: - query_expression_option_list query_expression_option - | query_expression_option - ; - query_expression_option: STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } | HIGH_PRIORITY { - if (unlikely(Lex->check_simple_select(&$1))) - MYSQL_YYABORT; YYPS->m_lock_type= TL_READ_HIGH_PRIORITY; YYPS->m_mdl_type= MDL_SHARED_READ; Select->options|= SELECT_HIGH_PRIORITY; @@ -17323,18 +17115,8 @@ query_expression_option: | DISTINCT { Select->options|= SELECT_DISTINCT; } | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } - | SQL_BUFFER_RESULT - { - if (unlikely(Lex->check_simple_select(&$1))) - MYSQL_YYABORT; - Select->options|= OPTION_BUFFER_RESULT; - } - | SQL_CALC_FOUND_ROWS - { - if (unlikely(Lex->check_simple_select(&$1))) - MYSQL_YYABORT; - Select->options|= OPTION_FOUND_ROWS; - } + | SQL_BUFFER_RESULT { Select->options|= OPTION_BUFFER_RESULT; } + | SQL_CALC_FOUND_ROWS { Select->options|= OPTION_FOUND_ROWS; } | ALL { Select->options|= SELECT_ALL; } ; @@ -17422,35 +17204,14 @@ view_select: lex->parsing_options.allows_variable= FALSE; lex->create_view->select.str= (char *) YYLIP->get_cpp_ptr(); } - opt_with_clause query_expression_body_view view_check_option + query_expression + view_check_option { - LEX *lex= Lex; - size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str; - void *create_view_select= thd->memdup(lex->create_view->select.str, len); - lex->create_view->select.length= len; - lex->create_view->select.str= (char *) create_view_select; - trim_whitespace(thd->charset(), - &lex->create_view->select); - lex->create_view->check= $4; - lex->parsing_options.allows_variable= TRUE; - lex->current_select->set_with_clause($2); + if (Lex->parsed_create_view($2, $3)) + MYSQL_YYABORT; } ; -/* - SQL Standard <query expression body> for VIEWs. - Does not include INTO and PROCEDURE clauses. -*/ -query_expression_body_view: - SELECT_SYM select_options_and_item_list select_init3_view - | table_value_constructor - | table_value_constructor union_order_or_limit - | table_value_constructor union_list_view - | '(' select_paren_view ')' - | '(' select_paren_view ')' union_order_or_limit - | '(' select_paren_view ')' union_list_view - ; - view_check_option: /* empty */ { $$= VIEW_CHECK_NONE; } | WITH CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; } @@ -17549,11 +17310,10 @@ trigger_tail: sp_proc_stmt alternatives are not saving/restoring LEX, so lex->query_tables can be wiped out. */ - if (unlikely(!lex->select_lex. - add_table_to_list(thd, $10, (LEX_CSTRING*) 0, - TL_OPTION_UPDATING, - TL_READ_NO_INSERT, - MDL_SHARED_NO_WRITE))) + if (!lex->first_select_lex()-> + add_table_to_list(thd, $10, (LEX_CSTRING*) 0, + TL_OPTION_UPDATING, TL_READ_NO_INSERT, + MDL_SHARED_NO_WRITE)) MYSQL_YYABORT; } ; @@ -17745,12 +17505,15 @@ opt_migrate: ; install: - INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys + INSTALL_SYM PLUGIN_SYM opt_if_not_exists ident SONAME_SYM TEXT_STRING_sys { LEX *lex= Lex; + lex->create_info.init(); + if (lex->add_create_options_with_check($3)) + MYSQL_YYABORT; lex->sql_command= SQLCOM_INSTALL_PLUGIN; - lex->comment= $3; - lex->ident= $5; + lex->comment= $4; + lex->ident= $6; } | INSTALL_SYM SONAME_SYM TEXT_STRING_sys { @@ -17762,18 +17525,24 @@ install: ; uninstall: - UNINSTALL_SYM PLUGIN_SYM ident + UNINSTALL_SYM PLUGIN_SYM opt_if_exists ident { LEX *lex= Lex; + lex->check_opt.init(); + if (lex->add_create_options_with_check($3)) + MYSQL_YYABORT; lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; - lex->comment= $3; + lex->comment= $4; } - | UNINSTALL_SYM SONAME_SYM TEXT_STRING_sys + | UNINSTALL_SYM SONAME_SYM opt_if_exists TEXT_STRING_sys { LEX *lex= Lex; + lex->check_opt.init(); + if (lex->add_create_options_with_check($3)) + MYSQL_YYABORT; lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; lex->comment= null_clex_str; - lex->ident= $3; + lex->ident= $4; } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index aaac4ba3e54..6d8f992760b 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -189,6 +189,20 @@ void ORAerror(THD *thd, const char *s) uint offset; } sp_cursor_name_and_offset; vers_history_point_t vers_history_point; + struct + { + enum sub_select_type unit_type; + bool distinct; + } unit_operation; + struct + { + SELECT_LEX *first; + SELECT_LEX *prev_last; + } select_list; + SQL_I_List<ORDER> *select_order; + Lex_select_lock select_lock; + Lex_select_limit select_limit; + Lex_order_limit_lock *order_limit_lock; /* pointers */ Create_field *create_field; @@ -234,6 +248,7 @@ void ORAerror(THD *thd, const char *s) handlerton *db_type; st_select_lex *select_lex; + st_select_lex_unit *select_lex_unit; struct p_elem_val *p_elem_value; class Window_frame *window_frame; class Window_frame_bound *window_frame_bound; @@ -243,7 +258,6 @@ void ORAerror(THD *thd, const char *s) /* enums */ enum enum_sp_suid_behaviour sp_suid; enum enum_view_suid view_suid; - enum sub_select_type unit_type; enum Condition_information_item::Name cond_info_item_name; enum enum_diag_condition_item_name diag_condition_item_name; enum Diagnostics_information::Which_area diag_area; @@ -282,10 +296,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 57 shift/reduce conflicts. + Currently there are 53 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 57 +%expect 53 /* Comments for TOKENS. @@ -436,6 +450,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token LEADING /* SQL-2003-R */ %token LEAVE_SYM %token LEFT /* SQL-2003-R */ +%token LEFT_PAREN_ALT /* INTERNAL */ +%token LEFT_PAREN_WITH /* INTERNAL */ +%token LEFT_PAREN_LIKE /* INTERNAL */ %token LEX_HOSTNAME %token LIKE /* SQL-2003-R */ %token LIMIT @@ -1195,7 +1212,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); NCHAR_STRING %type <lex_str_ptr> - opt_table_alias + opt_table_alias_clause + table_alias_clause %type <ident_cli> IDENT @@ -1263,7 +1281,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_temporary all_or_any opt_distinct opt_glimit_clause opt_ignore_leaves fulltext_options union_option opt_not - select_derived_init transaction_access_mode_types + transaction_access_mode_types opt_natural_language_mode opt_query_expansion opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt @@ -1386,11 +1404,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); join_table_list join_table table_factor table_ref esc_table_ref table_primary_ident table_primary_derived - select_derived derived_table_list - select_derived_union - derived_simple_table - derived_query_specification - derived_table_value_constructor + derived_table_list table_reference_list_parens + nested_table_reference_list join_table_parens %type <date_time_type> date_time_type; %type <interval> interval @@ -1426,14 +1441,19 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); UNDERSCORE_CHARSET %type <select_lex> subselect - get_select_lex get_select_lex_derived - simple_table query_specification - query_term_union_not_ready - query_term_union_ready - query_expression_body - select_paren_derived table_value_constructor + simple_table + query_primary + query_primary_parens + select_into_query_specification + + +%type <select_lex_unit> + query_specification_start + query_expression_body + query_expression + query_expression_unit %type <boolfunc2creator> comp_op @@ -1445,11 +1465,28 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type <virtual_column> opt_check_constraint check_constraint virtual_column_func column_default_expr -%type <unit_type> unit_type_decl + +%type <unit_operation> unit_type_decl + +%type <select_lock> + opt_procedure_or_into + opt_select_lock_type + select_lock_type + opt_lock_wait_timeout_new + +%type <select_limit> opt_limit_clause limit_clause limit_options + +%type <order_limit_lock> + query_expression_tail + order_or_limit + opt_order_limit_lock + +%type <select_order> opt_order_clause order_clause order_list %type <NONE> analyze_stmt_command - query verb_clause create change select do drop insert replace insert2 + query verb_clause create change select select_into + do drop insert replace insert2 insert_values update delete truncate rename compound_statement show describe load alter optimize keycache preload flush reset purge commit rollback savepoint release @@ -1466,7 +1503,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); preload_list preload_list_or_parts preload_keys preload_keys_parts select_item_list select_item values_list no_braces no_braces_with_names opt_values_with_names values_with_names - opt_limit_clause delete_limit_clause fields opt_values values + delete_limit_clause fields opt_values values procedure_list procedure_list2 procedure_item field_def handler opt_generated_always opt_ignore opt_column opt_restrict @@ -1486,9 +1523,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan single_multi table_wild_list table_wild_one opt_wild - union_clause union_list - subselect_start opt_and charset - subselect_end select_var_list select_var_list_init help + opt_and charset + select_var_list select_var_list_init help opt_extended_describe shutdown opt_format_json prepare prepare_src execute deallocate @@ -1639,8 +1675,8 @@ rule: <-- starts at col 1 query: END_OF_INPUT { - if (likely(!thd->bootstrap) && - unlikely(!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT))) + if (!thd->bootstrap && + (!(thd->lex->lex_options & OPTION_LEX_FOUND_COMMENT))) my_yyabort_error((ER_EMPTY_QUERY, MYF(0))); thd->lex->sql_command= SQLCOM_EMPTY_QUERY; @@ -1735,6 +1771,7 @@ statement: | rollback | savepoint | select + | select_into | set | set_assign | signal_stmt @@ -2089,17 +2126,22 @@ connection_name: /* create a table */ create: - create_or_replace opt_temporary TABLE_SYM opt_if_not_exists table_ident + create_or_replace opt_temporary TABLE_SYM opt_if_not_exists { LEX *lex= thd->lex; lex->create_info.init(); - if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, - $1 | $4))) + if (lex->main_select_push()) + MYSQL_YYABORT; + lex->current_select->parsing_place= BEFORE_OPT_LIST; + if (lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4)) MYSQL_YYABORT; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL, - TL_OPTION_UPDATING, - TL_WRITE, - MDL_EXCLUSIVE))) + } + table_ident + { + LEX *lex= thd->lex; + if (!lex->first_select_lex()-> + add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) MYSQL_YYABORT; lex->alter_info.reset(); /* @@ -2114,7 +2156,6 @@ create: create_body { LEX *lex= thd->lex; - lex->current_select= &lex->select_lex; if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && !lex->create_info.db_type) { @@ -2123,22 +2164,24 @@ create: ER_WARN_USING_OTHER_HANDLER, ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), hton_name(lex->create_info.db_type)->str, - $5->table.str); + $6->table.str); } create_table_set_open_action_and_adjust_tables(lex); + Lex->pop_select(); //main select } | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident { LEX *lex= thd->lex; + if (Lex->main_select_push()) + MYSQL_YYABORT; lex->create_info.init(); if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4))) MYSQL_YYABORT; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL, - TL_OPTION_UPDATING, - TL_WRITE, - MDL_EXCLUSIVE))) + if (!lex->first_select_lex()-> + add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) MYSQL_YYABORT; /* @@ -2161,8 +2204,9 @@ create: if (unlikely(lex->create_info.seq_create_info->check_and_adjust(1))) { my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), - lex->select_lex.table_list.first->db.str, - lex->select_lex.table_list.first->table_name.str); + lex->first_select_lex()->table_list.first->db.str, + lex->first_select_lex()->table_list.first-> + table_name.str); MYSQL_YYABORT; } @@ -2175,10 +2219,8 @@ create: Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; Lex->create_info.sequence= 1; - lex->current_select= &lex->select_lex; - if (unlikely((lex->create_info.used_fields & - HA_CREATE_USED_ENGINE) && - !lex->create_info.db_type)) + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) { lex->create_info.use_default_db_type(thd); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -2188,44 +2230,69 @@ create: $5->table.str); } create_table_set_open_action_and_adjust_tables(lex); + Lex->pop_select(); //main select } - | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident + | create_or_replace opt_unique INDEX_SYM opt_if_not_exists + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + ident opt_key_algorithm_clause ON table_ident { - if (unlikely(Lex->add_create_index_prepare($8))) + if (Lex->add_create_index_prepare($9)) MYSQL_YYABORT; - if (unlikely(Lex->add_create_index($2, &$5, $6, $1 | $4))) + if (Lex->add_create_index($2, &$6, $7, $1 | $4)) MYSQL_YYABORT; } '(' key_list ')' opt_lock_wait_timeout normal_key_options - opt_index_lock_algorithm { } - | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident + opt_index_lock_algorithm + { + Lex->pop_select(); //main select + } + | create_or_replace fulltext INDEX_SYM + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + opt_if_not_exists ident ON table_ident { - if (unlikely(Lex->add_create_index_prepare($7))) + if (Lex->add_create_index_prepare($8)) MYSQL_YYABORT; - if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, - $1 | $4))) + if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5)) MYSQL_YYABORT; } '(' key_list ')' opt_lock_wait_timeout fulltext_key_options - opt_index_lock_algorithm { } - | create_or_replace spatial INDEX_SYM opt_if_not_exists ident + opt_index_lock_algorithm + { + Lex->pop_select(); //main select + } + | create_or_replace spatial INDEX_SYM + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + opt_if_not_exists ident ON table_ident { - if (unlikely(Lex->add_create_index_prepare($7))) + if (Lex->add_create_index_prepare($8)) MYSQL_YYABORT; - if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, - $1 | $4))) + if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5)) MYSQL_YYABORT; } '(' key_list ')' opt_lock_wait_timeout spatial_key_options - opt_index_lock_algorithm { } + opt_index_lock_algorithm + { + Lex->pop_select(); //main select + } | create_or_replace DATABASE opt_if_not_exists ident { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; + if (Lex->main_select_push()) + MYSQL_YYABORT; } opt_create_database_options { @@ -2234,54 +2301,96 @@ create: $1 | $3))) MYSQL_YYABORT; lex->name= $4; + Lex->pop_select(); //main select } | create_or_replace definer_opt opt_view_suid VIEW_SYM opt_if_not_exists table_ident { - if (unlikely(Lex->add_create_view(thd, $1 | $5, - DTYPE_ALGORITHM_UNDEFINED, $3, - $6))) + if (Lex->main_select_push()) + MYSQL_YYABORT; + if (Lex->add_create_view(thd, $1 | $5, + DTYPE_ALGORITHM_UNDEFINED, $3, $6)) MYSQL_YYABORT; } view_list_opt AS view_select - { } + { + Lex->pop_select(); //main select + } | create_or_replace view_algorithm definer_opt opt_view_suid VIEW_SYM opt_if_not_exists table_ident { - if (unlikely(Lex->add_create_view(thd, $1 | $6, $2, $4, $7))) + if (Lex->main_select_push()) + MYSQL_YYABORT; + if (Lex->add_create_view(thd, $1 | $6, $2, $4, $7)) MYSQL_YYABORT; } view_list_opt AS view_select - { } + { + Lex->pop_select(); //main select + } | create_or_replace definer_opt TRIGGER_SYM - { Lex->create_info.set($1); } + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } trigger_tail - { } + { + Lex->pop_select(); //main select + } | create_or_replace definer_opt PROCEDURE_SYM - { Lex->create_info.set($1); } + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } sp_tail_standalone - { } + { + Lex->pop_select(); //main select + } | create_or_replace definer_opt EVENT_SYM - { Lex->create_info.set($1); } + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } event_tail - { } + { + Lex->pop_select(); //main select + } | create_or_replace definer FUNCTION_SYM - { Lex->create_info.set($1); } + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } sf_tail_standalone - { } + { + Lex->pop_select(); //main select + } | create_or_replace no_definer FUNCTION_SYM - { Lex->create_info.set($1); } + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } create_function_tail - { } + { + Lex->pop_select(); //main select + } | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM { + if (Lex->main_select_push()) + MYSQL_YYABORT; Lex->create_info.set($1); Lex->udf.type= UDFTYPE_AGGREGATE; } udf_tail - { } - | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list - opt_require_clause opt_resource_options + { + Lex->pop_select(); //main select + } + | create_or_replace USER_SYM opt_if_not_exists clear_privileges + grant_list opt_require_clause opt_resource_options { if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER, $1 | $3))) @@ -2902,7 +3011,7 @@ clear_privileges: lex->columns.empty(); lex->grant= lex->grant_tot_col= 0; lex->all_privileges= 0; - lex->select_lex.db= null_clex_str; + lex->first_select_lex()->db= null_clex_str; lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; bzero((char *)&(lex->mqh),sizeof(lex->mqh)); @@ -3532,7 +3641,7 @@ raise_stmt: signal_stmt: SIGNAL_SYM signal_value opt_set_signal_information { - if (unlikely(Lex->add_signal_statement(thd, $2))) + if (Lex->add_signal_statement(thd, $2)) MYSQL_YYABORT; } ; @@ -3979,7 +4088,9 @@ sp_proc_stmt_return: ; reset_lex_expr: - { Lex->sphead->reset_lex(thd); } expr { $$= $2; } + { Lex->sphead->reset_lex(thd); } + expr + { $$= $2; } ; sp_proc_stmt_exit: @@ -3995,14 +4106,14 @@ sp_proc_stmt_exit: } | EXIT_SYM WHEN_SYM reset_lex_expr { - if (unlikely(Lex->sp_exit_statement(thd, $3)) || - unlikely(Lex->sphead->restore_lex(thd))) + if (Lex->sp_exit_statement(thd, $3) || + Lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } | EXIT_SYM label_ident WHEN_SYM reset_lex_expr { - if (unlikely(Lex->sp_exit_statement(thd, &$2, $4)) || - unlikely(Lex->sphead->restore_lex(thd))) + if (Lex->sp_exit_statement(thd, &$2, $4) || + Lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } ; @@ -4020,14 +4131,14 @@ sp_proc_stmt_continue: } | CONTINUE_SYM WHEN_SYM reset_lex_expr { - if (unlikely(Lex->sp_continue_statement(thd, $3)) || - unlikely(Lex->sphead->restore_lex(thd))) + if (Lex->sp_continue_statement(thd, $3) || + Lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } | CONTINUE_SYM label_ident WHEN_SYM reset_lex_expr { - if (unlikely(Lex->sp_continue_statement(thd, &$2, $4)) || - unlikely(Lex->sphead->restore_lex(thd))) + if (Lex->sp_continue_statement(thd, &$2, $4) || + Lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } ; @@ -4086,7 +4197,7 @@ assignment_source_expr: $$->sp_lex_in_use= true; $$->set_item_and_free_list($3, thd->free_list); thd->free_list= NULL; - if (unlikely($$->sphead->restore_lex(thd))) + if ($$->sphead->restore_lex(thd)) MYSQL_YYABORT; } ; @@ -4095,6 +4206,7 @@ for_loop_bound_expr: assignment_source_lex { Lex->sphead->reset_lex(thd, $1); + Lex->current_select->parsing_place= FOR_LOOP_BOUND; } expr { @@ -4104,6 +4216,7 @@ for_loop_bound_expr: $$->set_item_and_free_list($3, NULL); if (unlikely($$->sphead->restore_lex(thd))) MYSQL_YYABORT; + Lex->current_select->parsing_place= NO_MATTER; } ; @@ -4325,7 +4438,8 @@ case_stmt_body: { if (unlikely(Lex->case_stmt_action_expr($2))) MYSQL_YYABORT; - if (unlikely(Lex->sphead->restore_lex(thd))) + + if (Lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } simple_when_clause_list @@ -4617,7 +4731,7 @@ while_body: LEX *lex= Lex; if (unlikely(lex->sp_while_loop_expression(thd, $1))) MYSQL_YYABORT; - if (unlikely(lex->sphead->restore_lex(thd))) + if (lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } sp_proc_stmts1 END LOOP_SYM @@ -4640,7 +4754,7 @@ repeat_body: if (unlikely(i == NULL) || unlikely(lex->sphead->add_instr(i))) MYSQL_YYABORT; - if (unlikely(lex->sphead->restore_lex(thd))) + if (lex->sphead->restore_lex(thd)) MYSQL_YYABORT; /* We can shortcut the cont_backpatch here */ i->m_cont_dest= ip+1; @@ -5120,26 +5234,16 @@ size_number: */ create_body: - '(' create_field_list ')' + create_field_list_parens { Lex->create_info.option_list= NULL; } opt_create_table_options opt_create_partitioning opt_create_select {} | opt_create_table_options opt_create_partitioning opt_create_select {} - /* - the following rule is redundant, but there's a shift/reduce - conflict that prevents the rule above from parsing a syntax like - CREATE TABLE t1 (SELECT 1); - */ - | '(' create_select_query_specification ')' - | '(' create_select_query_specification ')' - { Select->set_braces(1);} union_list {} - | '(' create_select_query_specification ')' - { Select->set_braces(1);} union_order_or_limit {} | create_like { Lex->create_info.add(DDL_options_st::OPT_LIKE); - TABLE_LIST *src_table= Lex->select_lex.add_table_to_list(thd, - $1, NULL, 0, TL_READ, MDL_SHARED_READ); + TABLE_LIST *src_table= Lex->first_select_lex()-> + add_table_to_list(thd, $1, NULL, 0, TL_READ, MDL_SHARED_READ); if (unlikely(! src_table)) MYSQL_YYABORT; /* CREATE TABLE ... LIKE is not allowed for views. */ @@ -5149,7 +5253,7 @@ create_body: create_like: LIKE table_ident { $$= $2; } - | '(' LIKE table_ident ')' { $$= $3; } + | LEFT_PAREN_LIKE LIKE table_ident ')' { $$= $3; } ; opt_create_select: @@ -5158,23 +5262,20 @@ opt_create_select: ; create_select_query_expression: - opt_with_clause SELECT_SYM create_select_part2 opt_table_expression - create_select_part4 - { - Select->set_braces(0); - Select->set_with_clause($1); + query_expression + { + if (Lex->parsed_insert_select($1->first_select())) + MYSQL_YYABORT; } - union_clause - | opt_with_clause SELECT_SYM create_select_part2 - create_select_part3_union_not_ready create_select_part4 + | LEFT_PAREN_WITH with_clause query_expression_body ')' { - Select->set_with_clause($1); + SELECT_LEX *first_select= $3->first_select(); + $3->set_with_clause($2); + $2->attach_to(first_select); + + if (Lex->parsed_insert_select(first_select)) + MYSQL_YYABORT; } - | '(' create_select_query_specification ')' - | '(' create_select_query_specification ')' - { Select->set_braces(1);} union_list {} - | '(' create_select_query_specification ')' - { Select->set_braces(1);} union_order_or_limit {} ; opt_create_partitioning: @@ -5262,8 +5363,13 @@ partition_entry: We enter here when opening the frm file to translate partition info string into part_info data structure. */ + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + partition + { + Lex->pop_select(); //main select } - partition {} ; partition: @@ -5874,7 +5980,7 @@ opt_versioning_rotation: | INTERVAL_SYM expr interval opt_versioning_interval_start { partition_info *part_info= Lex->part_info; - if (unlikely(part_info->vers_set_interval($2, $3, $4))) + if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4))) { my_error(ER_PART_WRONG_VALUE, MYF(0), Lex->create_last_non_select_table->table_name.str, @@ -5917,56 +6023,6 @@ opt_versioning_interval_start: End of partition parser part */ -create_select_query_specification: - opt_with_clause SELECT_SYM create_select_part2 create_select_part3 - create_select_part4 - { - Select->set_with_clause($1); - } - ; - -create_select_part2: - { - LEX *lex=Lex; - if (lex->sql_command == SQLCOM_INSERT) - lex->sql_command= SQLCOM_INSERT_SELECT; - else if (lex->sql_command == SQLCOM_REPLACE) - lex->sql_command= SQLCOM_REPLACE_SELECT; - /* - The following work only with the local list, the global list - is created correctly in this case - */ - lex->current_select->table_list.save_and_clear(&lex->save_list); - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; - } - select_options select_item_list - { - Select->parsing_place= NO_MATTER; - } - ; - -create_select_part3: - opt_table_expression - | create_select_part3_union_not_ready - ; - -create_select_part3_union_not_ready: - table_expression order_or_limit - | order_or_limit - ; - -create_select_part4: - opt_select_lock_type - { - /* - The following work only with the local list, the global list - is created correctly in this case - */ - Lex->current_select->table_list.push_front(&Lex->save_list); - } - ; - opt_as: /* empty */ {} | AS {} @@ -6184,7 +6240,7 @@ create_table_option: } | UNION_SYM opt_equal { - Lex->select_lex.table_list.save_and_clear(&Lex->save_list); + Lex->first_select_lex()->table_list.save_and_clear(&Lex->save_list); } '(' opt_table_list ')' { @@ -6193,8 +6249,8 @@ create_table_option: from the global list. */ LEX *lex=Lex; - lex->create_info.merge_list= lex->select_lex.table_list; - lex->select_lex.table_list= lex->save_list; + lex->create_info.merge_list= lex->first_select_lex()->table_list; + lex->first_select_lex()->table_list= lex->save_list; /* When excluding union list from the global list we assume that elements of the former immediately follow elements which represent @@ -6395,6 +6451,13 @@ create_field_list: } ; +create_field_list_parens: + LEFT_PAREN_ALT field_list ')' + { + Lex->create_last_non_select_table= Lex->last_table(); + } + ; + field_list: field_list_item | field_list ',' field_list_item @@ -6689,6 +6752,8 @@ parse_vcol_expr: Prevent the end user from invoking this command. */ MYSQL_YYABORT_UNLESS(Lex->parse_vcol_expr); + if (Lex->main_select_push()) + MYSQL_YYABORT; } expr { @@ -6696,14 +6761,15 @@ parse_vcol_expr: if (unlikely(!v)) MYSQL_YYABORT; Lex->last_field->vcol_info= v; + Lex->pop_select(); //main select } ; parenthesized_expr: - subselect + remember_tok_start + query_expression { - $$= new (thd->mem_root) Item_singlerow_subselect(thd, $1); - if (unlikely($$ == NULL)) + if (!($$= Lex->create_item_query_expression(thd, $1, $2))) MYSQL_YYABORT; } | expr @@ -7729,23 +7795,25 @@ alter: Lex->name= null_clex_str; Lex->table_type= TABLE_TYPE_UNKNOWN; Lex->sql_command= SQLCOM_ALTER_TABLE; - Lex->duplicates= DUP_ERROR; - Lex->select_lex.init_order(); + Lex->duplicates= DUP_ERROR; + Lex->first_select_lex()->order_list.empty(); Lex->create_info.init(); Lex->create_info.row_type= ROW_TYPE_NOT_USED; Lex->alter_info.reset(); Lex->no_write_to_binlog= 0; Lex->create_info.storage_media= HA_SM_DEFAULT; + if (Lex->main_select_push()) + MYSQL_YYABORT; DBUG_ASSERT(!Lex->m_sql_cmd); } alter_options TABLE_SYM table_ident opt_lock_wait_timeout { - if (unlikely(!Lex->select_lex.add_table_to_list(thd, $5, NULL, - TL_OPTION_UPDATING, - TL_READ_NO_INSERT, - MDL_SHARED_UPGRADABLE))) + if (!Lex->first_select_lex()-> + add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, + TL_READ_NO_INSERT, MDL_SHARED_UPGRADABLE)) MYSQL_YYABORT; - Lex->select_lex.db= (Lex->select_lex.table_list.first)->db; + Lex->first_select_lex()->db= + (Lex->first_select_lex()->table_list.first)->db; Lex->create_last_non_select_table= Lex->last_table(); } alter_commands @@ -7757,11 +7825,14 @@ alter: if (unlikely(Lex->m_sql_cmd == NULL)) MYSQL_YYABORT; } + Lex->pop_select(); //main select } | ALTER DATABASE ident_or_empty { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; + if (Lex->main_select_push()) + MYSQL_YYABORT; } create_database_options { @@ -7771,6 +7842,7 @@ alter: if (lex->name.str == NULL && unlikely(lex->copy_db_to(&lex->name))) MYSQL_YYABORT; + Lex->pop_select(); //main select } | ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM { @@ -7786,6 +7858,8 @@ alter: if (unlikely(lex->sphead)) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE")); + if (Lex->main_select_push()) + MYSQL_YYABORT; lex->sp_chistics.init(); } sp_a_chistics @@ -7794,6 +7868,9 @@ alter: lex->sql_command= SQLCOM_ALTER_PROCEDURE; lex->spname= $3; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } | ALTER FUNCTION_SYM sp_name { @@ -7801,6 +7878,8 @@ alter: if (unlikely(lex->sphead)) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); + if (Lex->main_select_push()) + MYSQL_YYABORT; lex->sp_chistics.init(); } sp_a_chistics @@ -7809,14 +7888,23 @@ alter: lex->sql_command= SQLCOM_ALTER_FUNCTION; lex->spname= $3; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } | ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident { - if (unlikely(Lex->add_alter_view(thd, $2, $4, $6))) + if (Lex->main_select_push()) + MYSQL_YYABORT; + if (Lex->add_alter_view(thd, $2, $4, $6)) MYSQL_YYABORT; } view_list_opt AS view_select - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } | ALTER definer_opt opt_view_suid VIEW_SYM table_ident /* We have two separate rules for ALTER VIEW rather that @@ -7824,14 +7912,22 @@ alter: with the ALTER EVENT below. */ { - if (unlikely(Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5))) + if (Lex->main_select_push()) + MYSQL_YYABORT; + if (Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5)) MYSQL_YYABORT; } view_list_opt AS view_select - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } | ALTER definer_opt remember_name EVENT_SYM sp_name { - /* + if (Lex->main_select_push()) + MYSQL_YYABORT; + /* It is safe to use Lex->spname because ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO is not allowed. Lex->spname is used in the case of RENAME TO @@ -7863,6 +7959,8 @@ alter: */ Lex->sql_command= SQLCOM_ALTER_EVENT; Lex->stmt_definition_end= (char*)YYLIP->get_cpp_ptr(); + + Lex->pop_select(); //main select } | ALTER TABLESPACE alter_tablespace_info { @@ -7906,16 +8004,17 @@ alter: lex->create_info.init(); lex->no_write_to_binlog= 0; DBUG_ASSERT(!lex->m_sql_cmd); + if (Lex->main_select_push()) + MYSQL_YYABORT; } table_ident { LEX *lex= Lex; - if (unlikely(!(lex->create_info.seq_create_info= - new (thd->mem_root) sequence_definition())) || - unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL, - TL_OPTION_SEQUENCE, - TL_WRITE, - MDL_EXCLUSIVE))) + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition()) || + !lex->first_select_lex()-> + add_table_to_list(thd, $5, NULL, TL_OPTION_SEQUENCE, + TL_WRITE, MDL_EXCLUSIVE)) MYSQL_YYABORT; } sequence_defs @@ -7924,6 +8023,9 @@ alter: Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_sequence($3); if (unlikely(Lex->m_sql_cmd == NULL)) MYSQL_YYABORT; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } ; @@ -8073,16 +8175,14 @@ alter_commands: WITH TABLE_SYM table_ident have_partitioning { LEX *lex= thd->lex; - lex->select_lex.db= $6->db; - if (lex->select_lex.db.str == NULL && - unlikely(lex->copy_db_to(&lex->select_lex.db))) + if (lex->first_select_lex()->db.str == NULL && + lex->copy_db_to(&lex->first_select_lex()->db)) MYSQL_YYABORT; lex->name= $6->table; lex->alter_info.partition_flags|= ALTER_PARTITION_EXCHANGE; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $6, NULL, - TL_OPTION_UPDATING, - TL_READ_NO_INSERT, - MDL_SHARED_NO_WRITE))) + if (!lex->first_select_lex()-> + add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING, + TL_READ_NO_INSERT, MDL_SHARED_NO_WRITE)) MYSQL_YYABORT; DBUG_ASSERT(!lex->m_sql_cmd); lex->m_sql_cmd= new (thd->mem_root) @@ -8321,10 +8421,11 @@ alter_list_item: | RENAME opt_to table_ident { LEX *lex=Lex; - lex->select_lex.db= $3->db; - if (lex->select_lex.db.str == NULL && - unlikely(lex->copy_db_to(&lex->select_lex.db))) + lex->first_select_lex()->db= $3->db; + if (lex->first_select_lex()->db.str == NULL && + lex->copy_db_to(&lex->first_select_lex()->db)) MYSQL_YYABORT; + if (unlikely(check_table_name($3->table.str,$3->table.length, FALSE)) || ($3->db.str && unlikely(check_db_name((LEX_STRING*) &$3->db)))) @@ -9018,8 +9119,8 @@ adm_partition: cache_keys_spec: { - Lex->select_lex.alloc_index_hints(thd); - Select->set_index_hint_type(INDEX_HINT_USE, + Lex->first_select_lex()->alloc_index_hints(thd); + Select->set_index_hint_type(INDEX_HINT_USE, INDEX_HINT_MASK_ALL); } cache_key_list_or_empty @@ -9042,215 +9143,211 @@ opt_ignore_leaves: select: - opt_with_clause select_init - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_SELECT; - lex->current_select->set_with_clause($1); - } - ; - -select_init: - SELECT_SYM select_options_and_item_list select_init3 - | table_value_constructor - | table_value_constructor union_list - | table_value_constructor union_order_or_limit - | '(' select_paren ')' - | '(' select_paren ')' union_list - | '(' select_paren ')' union_order_or_limit - ; - -union_list_part2: - SELECT_SYM select_options_and_item_list select_init3_union_query_term - | table_value_constructor - | table_value_constructor union_list - | table_value_constructor union_order_or_limit - | '(' select_paren_union_query_term ')' - | '(' select_paren_union_query_term ')' union_list - | '(' select_paren_union_query_term ')' union_order_or_limit - ; - -select_paren: + query_expression_body { - Lex->current_select->set_braces(true); + if (Lex->push_select($1->fake_select_lex ? + $1->fake_select_lex : + $1->first_select())) + MYSQL_YYABORT; } - table_value_constructor + opt_procedure_or_into { - DBUG_ASSERT(Lex->current_select->braces); + Lex->pop_select(); + if (Lex->select_finalize($1)) + MYSQL_YYABORT; } - | + | with_clause query_expression_body { - /* - In order to correctly parse UNION's global ORDER BY we need to - set braces before parsing the clause. - */ - Lex->current_select->set_braces(true); + if (Lex->push_select($2->fake_select_lex ? + $2->fake_select_lex : + $2->first_select())) + MYSQL_YYABORT; } - SELECT_SYM select_options_and_item_list select_part3 - opt_select_lock_type + opt_procedure_or_into { - DBUG_ASSERT(Lex->current_select->braces); + Lex->pop_select(); + $2->set_with_clause($1); + $1->attach_to($2->first_select()); + if (Lex->select_finalize($2)) + MYSQL_YYABORT; } - | '(' select_paren ')' ; -select_paren_union_query_term: + +select_into: + select_into_query_specification { - /* - In order to correctly parse UNION's global ORDER BY we need to - set braces before parsing the clause. - */ - Lex->current_select->set_braces(true); + if (Lex->push_select($1)) + MYSQL_YYABORT; } - SELECT_SYM select_options_and_item_list select_part3_union_query_term - opt_select_lock_type + opt_order_limit_lock { - DBUG_ASSERT(Lex->current_select->braces); - } - | '(' select_paren_union_query_term ')' - ; + st_select_lex_unit *unit; + if (!(unit= Lex->parsed_body_select($1, $3))) + MYSQL_YYABORT; + if (Lex->select_finalize(unit)) + MYSQL_YYABORT; + } + ; + -select_paren_view: +simple_table: + query_specification { $$= $1; } + | table_value_constructor { $$= $1; } + ; + +table_value_constructor: + VALUES + { + if (Lex->parsed_TVC_start()) + MYSQL_YYABORT; + } + values_list + { + if (!($$= Lex->parsed_TVC_end())) + MYSQL_YYABORT; + } + ; + +query_specification_start: + SELECT_SYM { - /* - In order to correctly parse UNION's global ORDER BY we need to - set braces before parsing the clause. - */ - Lex->current_select->set_braces(true); + SELECT_LEX *sel; + LEX *lex= Lex; + if (!(sel= lex->alloc_select(TRUE)) || + lex->push_select(sel)) + MYSQL_YYABORT; + sel->init_select(); + sel->braces= FALSE; } - SELECT_SYM select_options_and_item_list select_part3_view - opt_select_lock_type + select_options { - DBUG_ASSERT(Lex->current_select->braces); + Select->parsing_place= SELECT_LIST; } - | '(' select_paren_view ')' - ; + select_item_list + { + Select->parsing_place= NO_MATTER; + } + ; -/* The equivalent of select_paren for nested queries. */ -select_paren_derived: +query_specification: + query_specification_start + opt_from_clause + opt_where_clause + opt_group_clause + opt_having_clause + opt_window_clause { - Lex->current_select->set_braces(true); + $$= Lex->pop_select(); } - table_value_constructor + ; + +select_into_query_specification: + query_specification_start + into + opt_from_clause + opt_where_clause + opt_group_clause + opt_having_clause + opt_window_clause { - DBUG_ASSERT(Lex->current_select->braces); - $$= Lex->current_select->master_unit()->first_select(); + $$= Lex->pop_select(); } - | + ; + +opt_from_clause: + /* Empty */ + | from_clause + ; + +query_primary: + simple_table + { $$= $1; } + | query_primary_parens + { $$= $1; } + ; + +query_primary_parens: + '(' query_expression_unit { - Lex->current_select->set_braces(true); + if (Lex->parsed_unit_in_brackets($2)) + MYSQL_YYABORT; } - SELECT_SYM select_part2_derived - opt_table_expression - opt_order_clause - opt_limit_clause - opt_select_lock_type + query_expression_tail ')' { - DBUG_ASSERT(Lex->current_select->braces); - $$= Lex->current_select->master_unit()->first_select(); + $$= Lex->parsed_unit_in_brackets_tail($2, $4); } - | '(' select_paren_derived ')' { $$= $2; } - ; - -select_init3: - opt_table_expression - opt_select_lock_type + | '(' query_primary { - /* Parentheses carry no meaning here */ - Lex->current_select->set_braces(false); + Lex->push_select($2); } - union_clause - | select_part3_union_not_ready - opt_select_lock_type + query_expression_tail ')' { - /* Parentheses carry no meaning here */ - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_select_in_brackets($2, $4))) + YYABORT; } ; - -select_init3_union_query_term: - opt_table_expression - opt_select_lock_type +query_expression_unit: + query_primary + unit_type_decl + query_primary { - /* Parentheses carry no meaning here */ - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_select_expr_start($1, $3, $2.unit_type, + $2.distinct))) + YYABORT; } - union_clause - | select_part3_union_not_ready_noproc - opt_select_lock_type + | query_expression_unit + unit_type_decl + query_primary { - /* Parentheses carry no meaning here */ - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_select_expr_cont($1, $3, $2.unit_type, + $2.distinct, TRUE))) + YYABORT; } ; - -select_init3_view: - opt_table_expression opt_select_lock_type +query_expression_body: + query_primary { - Lex->current_select->set_braces(false); + Lex->push_select($1); } - | opt_table_expression opt_select_lock_type + query_expression_tail { - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_body_select($1, $3))) + MYSQL_YYABORT; } - union_list_view - | order_or_limit opt_select_lock_type + | query_expression_unit { - Lex->current_select->set_braces(false); + if (Lex->parsed_body_unit($1)) + MYSQL_YYABORT; } - | table_expression order_or_limit opt_select_lock_type + query_expression_tail { - Lex->current_select->set_braces(false); + if (!($$= Lex->parsed_body_unit_tail($1, $3))) + MYSQL_YYABORT; } ; -/* - The SELECT parts after select_item_list that cannot be followed by UNION. -*/ - -select_part3: - opt_table_expression - | select_part3_union_not_ready - ; - -select_part3_union_query_term: - opt_table_expression - | select_part3_union_not_ready_noproc - ; - -select_part3_view: - opt_table_expression - | order_or_limit - | table_expression order_or_limit - ; - -select_part3_union_not_ready: - select_part3_union_not_ready_noproc - | table_expression procedure_clause - | table_expression order_or_limit procedure_clause - ; - -select_part3_union_not_ready_noproc: - order_or_limit - | into opt_table_expression opt_order_clause opt_limit_clause - | table_expression into - | table_expression order_or_limit - | table_expression order_or_limit into - ; - -select_options_and_item_list: +query_expression: + opt_with_clause + query_expression_body { - LEX *lex= Lex; - SELECT_LEX *sel= lex->current_select; - if (sel->linkage != UNION_TYPE) - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; + if ($1) + { + $2->set_with_clause($1); + $1->attach_to($2->first_select()); + } + $$= $2; } - select_options select_item_list + ; + +subselect: + remember_tok_start + query_expression { - Select->parsing_place= NO_MATTER; + if (!($$= Lex->parsed_subselect($2, $1))) + YYABORT; } ; @@ -9258,18 +9355,6 @@ select_options_and_item_list: /** <table expression>, as in the SQL standard. */ -table_expression: - from_clause - opt_where_clause - opt_group_clause - opt_having_clause - opt_window_clause - ; - -opt_table_expression: - /* Empty */ - | table_expression - ; from_clause: FROM table_reference_list @@ -9318,8 +9403,9 @@ history_point: TIMESTAMP TEXT_STRING { Item *item; - if (!(item= create_temporal_literal(thd, $2.str, $2.length, YYCSCL, - MYSQL_TYPE_DATETIME, true))) + if (!(item= type_handler_datetime2.create_literal_item(thd, + $2.str, $2.length, + YYCSCL, true))) MYSQL_YYABORT; $$= Vers_history_point(VERS_TIMESTAMP, item); } @@ -9372,59 +9458,70 @@ select_option: query_expression_option | SQL_NO_CACHE_SYM { - /* - Allow this flag only on the first top-level SELECT statement, if - SQL_CACHE wasn't specified, and only once per query. - */ - if (unlikely(Lex->current_select != &Lex->select_lex)) - my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE")); - if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE)) - my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE")); - if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE)) + /* + Allow this flag once per query. + */ + if (Select->options & OPTION_NO_QUERY_CACHE) my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE")); - - Lex->safe_to_cache_query=0; - Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE; - Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE; + Select->options|= OPTION_NO_QUERY_CACHE; } | SQL_CACHE_SYM { - /* - Allow this flag only on the first top-level SELECT statement, if - SQL_NO_CACHE wasn't specified, and only once per query. - */ - if (unlikely(Lex->current_select != &Lex->select_lex)) - my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE")); - if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE)) - my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE")); - if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE)) + /* + Allow this flag once per query. + */ + if (Select->options & OPTION_TO_QUERY_CACHE) my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE")); - - Lex->safe_to_cache_query=1; - Lex->select_lex.options|= OPTION_TO_QUERY_CACHE; - Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE; + Select->options|= OPTION_TO_QUERY_CACHE; } ; -opt_select_lock_type: - /* empty */ - | FOR_SYM UPDATE_SYM opt_lock_wait_timeout + +select_lock_type: + FOR_SYM UPDATE_SYM opt_lock_wait_timeout_new { - LEX *lex=Lex; - lex->current_select->lock_type= TL_WRITE; - lex->current_select->set_lock_for_tables(TL_WRITE); - lex->safe_to_cache_query=0; + $$= $3; + $$.defined_lock= TRUE; + $$.update_lock= TRUE; } - | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout_new { - LEX *lex=Lex; - lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS; - lex->current_select-> - set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS); - lex->safe_to_cache_query=0; + $$= $5; + $$.defined_lock= TRUE; + $$.update_lock= FALSE; } ; + +opt_select_lock_type: + /* empty */ + { + $$.empty(); + } + | select_lock_type + { + $$= $1; + } + ; + + +opt_lock_wait_timeout_new: + /* empty */ + { + $$.empty(); + } + | WAIT_SYM ulong_num + { + $$.defined_timeout= TRUE; + $$.timeout= $2; + } + | NOWAIT_SYM + { + $$.defined_timeout= TRUE; + $$.timeout= 0; + } + ; + select_item_list: select_item_list ',' select_item | select_item @@ -10127,7 +10224,21 @@ column_default_non_parenthesized_expr: | param_marker { $$= $1; } | variable | sum_expr + { + if (!Lex->select_stack_top) + { + my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0)); + MYSQL_YYABORT; + } + } | window_func_expr + { + if (!Lex->select_stack_top) + { + my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0)); + MYSQL_YYABORT; + } + } | inverse_distribution_function | ROW_SYM '(' expr ',' expr_list ')' { @@ -10324,7 +10435,7 @@ function_call_keyword_timestamp: } | TIMESTAMP '(' expr ',' expr ')' { - $$= new (thd->mem_root) Item_func_add_time(thd, $3, $5, 1, 0); + $$= new (thd->mem_root) Item_func_timestamp(thd, $3, $5); if (unlikely($$ == NULL)) MYSQL_YYABORT; } @@ -11756,10 +11867,15 @@ esc_table_ref: /* Equivalent to <table reference list> in the SQL:2003 standard. */ /* Warning - may return NULL in case of incomplete SELECT */ derived_table_list: - esc_table_ref { $$=$1; } + esc_table_ref + { + $$=$1; + Select->add_joined_table($1); + } | derived_table_list ',' esc_table_ref { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); + Select->add_joined_table($3); } ; @@ -11778,11 +11894,18 @@ join_table: left-associative joins. */ table_ref normal_join table_ref %prec TABLE_REF_PRIORITY - { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=$2; } + { + MYSQL_YYABORT_UNLESS($1 && ($$=$3)); + Select->add_joined_table($1); + Select->add_joined_table($3); + $3->straight=$2; + } | table_ref normal_join table_ref ON { MYSQL_YYABORT_UNLESS($1 && $3); + Select->add_joined_table($1); + Select->add_joined_table($3); /* Change the current name resolution context to a local context. */ if (unlikely(push_new_name_resolution_context(thd, $1, $3))) MYSQL_YYABORT; @@ -11799,6 +11922,8 @@ join_table: USING { MYSQL_YYABORT_UNLESS($1 && $3); + Select->add_joined_table($1); + Select->add_joined_table($3); } '(' using_list ')' { @@ -11809,6 +11934,8 @@ join_table: | table_ref NATURAL inner_join table_factor { MYSQL_YYABORT_UNLESS($1 && ($$=$4)); + Select->add_joined_table($1); + Select->add_joined_table($4); $4->straight=$3; add_join_natural($1,$4,NULL,Select); } @@ -11818,6 +11945,8 @@ join_table: ON { MYSQL_YYABORT_UNLESS($1 && $5); + Select->add_joined_table($1); + Select->add_joined_table($5); /* Change the current name resolution context to a local context. */ if (unlikely(push_new_name_resolution_context(thd, $1, $5))) MYSQL_YYABORT; @@ -11834,6 +11963,8 @@ join_table: | table_ref LEFT opt_outer JOIN_SYM table_factor { MYSQL_YYABORT_UNLESS($1 && $5); + Select->add_joined_table($1); + Select->add_joined_table($5); } USING '(' using_list ')' { @@ -11844,6 +11975,8 @@ join_table: | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor { MYSQL_YYABORT_UNLESS($1 && $6); + Select->add_joined_table($1); + Select->add_joined_table($6); add_join_natural($1,$6,NULL,Select); $6->outer_join|=JOIN_TYPE_LEFT; $$=$6; @@ -11854,6 +11987,8 @@ join_table: ON { MYSQL_YYABORT_UNLESS($1 && $5); + Select->add_joined_table($1); + Select->add_joined_table($5); /* Change the current name resolution context to a local context. */ if (unlikely(push_new_name_resolution_context(thd, $1, $5))) MYSQL_YYABORT; @@ -11871,6 +12006,8 @@ join_table: | table_ref RIGHT opt_outer JOIN_SYM table_factor { MYSQL_YYABORT_UNLESS($1 && $5); + Select->add_joined_table($1); + Select->add_joined_table($5); } USING '(' using_list ')' { @@ -11882,6 +12019,8 @@ join_table: | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor { MYSQL_YYABORT_UNLESS($1 && $6); + Select->add_joined_table($1); + Select->add_joined_table($6); add_join_natural($6,$1,NULL,Select); LEX *lex= Lex; if (unlikely(!($$= lex->current_select->convert_right_join()))) @@ -11916,238 +12055,45 @@ use_partition: $$= $3; } ; - -/* - This is a flattening of the rules <table factor> and <table primary> - in the SQL:2003 standard, since we don't have <sample clause> - I.e. - <table factor> ::= <table primary> [ <sample clause> ] -*/ -/* Warning - may return NULL in case of incomplete SELECT */ table_factor: - table_primary_ident - | table_primary_derived - ; - -table_primary_ident: - { - DBUG_ASSERT(Select); - SELECT_LEX *sel= Select; - sel->table_join_options= 0; - } - table_ident opt_use_partition opt_for_system_time_clause opt_table_alias opt_key_definition - { - if (unlikely(!($$= Select->add_table_to_list(thd, $2, $5, - Select->get_table_join_options(), - YYPS->m_lock_type, - YYPS->m_mdl_type, - Select-> - pop_index_hints(), - $3)))) - MYSQL_YYABORT; - Select->add_joined_table($$); - if ($4) - $$->vers_conditions= Lex->vers_conditions; - } + table_primary_ident { $$= $1; } + | table_primary_derived { $$= $1; } + | join_table_parens { $$= $1; } + | table_reference_list_parens { $$= $1; } ; - - -/* - Represents a flattening of the following rules from the SQL:2003 - standard. This sub-rule corresponds to the sub-rule - <table primary> ::= ... | <derived table> [ AS ] <correlation name> - - <derived table> ::= <table subquery> - <table subquery> ::= <subquery> - <subquery> ::= <left paren> <query expression> <right paren> - <query expression> ::= [ <with clause> ] <query expression body> - - For the time being we use the non-standard rule - select_derived_union which is a compromise between the standard - and our parser. Possibly this rule could be replaced by our - query_expression_body. -*/ - -table_primary_derived: - '(' get_select_lex select_derived_union ')' opt_for_system_time_clause opt_table_alias +table_reference_list_parens: + '(' table_reference_list_parens ')' { $$= $2; } + | '(' nested_table_reference_list ')' { - /* Use $2 instead of Lex->current_select as derived table will - alter value of Lex->current_select. */ - if (!($3 || $6) && $2->embedding && - !$2->embedding->nested_join->join_list.elements) - { - /* we have a derived table ($3 == NULL) but no alias, - Since we are nested in further parentheses so we - can pass NULL to the outer level parentheses - Permits parsing of "((((select ...))) as xyz)" */ - $$= 0; - } - else if (!$3) - { - /* Handle case of derived table, alias may be NULL if there - are no outer parentheses, add_table_to_list() will throw - error in this case */ - LEX *lex=Lex; - lex->check_automatic_up(UNSPECIFIED_TYPE); - SELECT_LEX *sel= lex->current_select; - SELECT_LEX_UNIT *unit= sel->master_unit(); - lex->current_select= sel= unit->outer_select(); - Table_ident *ti= new (thd->mem_root) Table_ident(unit); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!($$= sel->add_table_to_list(thd, - ti, $6, 0, - TL_READ, - MDL_SHARED_READ)))) - MYSQL_YYABORT; - sel->add_joined_table($$); - lex->pop_context(); - lex->nest_level--; - } - else if (unlikely($6 != NULL)) - { - /* - Tables with or without joins within parentheses cannot - have aliases, and we ruled out derived tables above. - */ - thd->parse_error(); + if (!($$= Select->end_nested_join(thd))) MYSQL_YYABORT; - } - else - { - /* nested join: FROM (t1 JOIN t2 ...), - nest_level is the same as in the outer query */ - $$= $3; - } - /* - Fields in derived table can be used in upper select in - case of merge. We do not add HAVING fields because we do - not merge such derived. We do not add union because - also do not merge them - */ - if ($$ && $$->derived && - !$$->derived->first_select()->next_select()) - $$->select_lex->add_where_field($$->derived->first_select()); - if ($5) - { - MYSQL_YYABORT_UNLESS(!$3); - $$->vers_conditions= Lex->vers_conditions; - } } - /* Represents derived table with WITH clause */ - | '(' get_select_lex subselect_start - with_clause query_expression_body - subselect_end ')' opt_for_system_time_clause opt_table_alias - { - LEX *lex=Lex; - SELECT_LEX *sel= $2; - SELECT_LEX_UNIT *unit= $5->master_unit(); - Table_ident *ti= new (thd->mem_root) Table_ident(unit); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - $5->set_with_clause($4); - lex->current_select= sel; - if (unlikely(!($$= sel->add_table_to_list(lex->thd, - ti, $9, 0, - TL_READ, - MDL_SHARED_READ)))) - MYSQL_YYABORT; - sel->add_joined_table($$); - if ($8) - $$->vers_conditions= Lex->vers_conditions; - } ; -/* - This rule accepts just about anything. The reason is that we have - empty-producing rules in the beginning of rules, in this case - subselect_start. This forces bison to take a decision which rules to - reduce by long before it has seen any tokens. This approach ties us - to a very limited class of parseable languages, and unfortunately - SQL is not one of them. The chosen 'solution' was this rule, which - produces just about anything, even complete bogus statements, for - instance ( table UNION SELECT 1 ). - Fortunately, we know that the semantic value returned by - select_derived is NULL if it contained a derived table, and a pointer to - the base table's TABLE_LIST if it was a base table. So in the rule - regarding union's, we throw a parse error manually and pretend it - was bison that did it. - - Also worth noting is that this rule concerns query expressions in - the from clause only. Top level select statements and other types of - subqueries have their own union rules. -*/ -select_derived_union: - select_derived - | select_derived union_order_or_limit +nested_table_reference_list: + table_ref ',' table_ref { - if (unlikely($1)) - { - thd->parse_error(); + if (Select->init_nested_join(thd)) MYSQL_YYABORT; - } + Select->add_joined_table($1); + Select->add_joined_table($3); + $$= $1->embedding; } - | select_derived union_head_non_top + | nested_table_reference_list ',' table_ref { - if (unlikely($1)) - { - thd->parse_error(); - MYSQL_YYABORT; - } - } - union_list_derived_part2 - | derived_simple_table opt_select_lock_type - | derived_simple_table order_or_limit opt_select_lock_type - | derived_simple_table opt_select_lock_type union_list_derived - ; - -union_list_derived_part2: - query_term_union_not_ready { Lex->pop_context(); } - | query_term_union_ready { Lex->pop_context(); } - | query_term_union_ready { Lex->pop_context(); } union_list_derived - ; - -union_list_derived: - union_head_non_top union_list_derived_part2 - ; - - -/* The equivalent of select_init2 for nested queries. */ -select_init2_derived: - select_part2_derived - { - Select->set_braces(0); - } - ; - -/* The equivalent of select_part2 for nested queries. */ -select_part2_derived: - { - LEX *lex= Lex; - SELECT_LEX *sel= lex->current_select; - if (sel->linkage != UNION_TYPE) - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; - } - opt_query_expression_options select_item_list - { - Select->parsing_place= NO_MATTER; + Select->add_joined_table($3); + $$= $1; } ; -/* handle contents of parentheses in join expression */ -select_derived: - get_select_lex_derived derived_table_list +join_table_parens: + '(' join_table_parens ')' { $$= $2; } + | '(' join_table ')' { LEX *lex= Lex; - /* for normal joins, $2 != NULL and end_nested_join() != NULL, - for derived tables, both must equal NULL */ - - if (unlikely(!($$= $1->end_nested_join(lex->thd)) && $2)) - MYSQL_YYABORT; - if (unlikely(!$2 && $$)) + if (!($$= lex->current_select->nest_last_join(thd))) { thd->parse_error(); MYSQL_YYABORT; @@ -12155,83 +12101,54 @@ select_derived: } ; -derived_simple_table: - derived_query_specification { $$= $1; } - | derived_table_value_constructor { $$= $1; } - ; -/* - Similar to query_specification, but for derived tables. - Example: the inner parenthesized SELECT in this query: - SELECT * FROM (SELECT * FROM t1); -*/ -derived_query_specification: - SELECT_SYM select_derived_init select_derived2 - { - if ($2) - Select->set_braces(1); - $$= NULL; - } - ; -derived_table_value_constructor: - VALUES - { - Lex->tvc_start(); - } - values_list +table_primary_ident: + table_ident opt_use_partition opt_for_system_time_clause + opt_table_alias_clause opt_key_definition { - if (Lex->tvc_finalize_derived()) + SELECT_LEX *sel= Select; + sel->table_join_options= 0; + if (!($$= Select->add_table_to_list(thd, $1, $4, + Select->get_table_join_options(), + YYPS->m_lock_type, + YYPS->m_mdl_type, + Select->pop_index_hints(), + $2))) MYSQL_YYABORT; - $$= NULL; + if ($3) + $$->vers_conditions= Lex->vers_conditions; } ; -select_derived2: - { - LEX *lex= Lex; - lex->derived_tables|= DERIVED_SUBQUERY; - if (unlikely(!lex->expr_allows_subselect || - lex->sql_command == (int)SQLCOM_PURGE)) - { - thd->parse_error(); - MYSQL_YYABORT; - } - if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || - unlikely(mysql_new_select(lex, 1, NULL))) - MYSQL_YYABORT; - mysql_init_select(lex); - lex->current_select->linkage= DERIVED_TABLE_TYPE; - lex->current_select->parsing_place= SELECT_LIST; - } - select_options select_item_list - { - Select->parsing_place= NO_MATTER; - } - opt_table_expression - ; +/* + Represents a flattening of the following rules from the SQL:2003 + standard. This sub-rule corresponds to the sub-rule + <table primary> ::= ... | <derived table> [ AS ] <correlation name> -get_select_lex: - /* Empty */ { $$= Select; } - ; + <derived table> ::= <table subquery> + <table subquery> ::= <subquery> + <subquery> ::= <left paren> <query expression> <right paren> + <query expression> ::= [ <with clause> ] <query expression body> + + For the time being we use the non-standard rule + select_derived_union which is a compromise between the standard + and our parser. Possibly this rule could be replaced by our + query_expression_body. +*/ -get_select_lex_derived: - get_select_lex +table_primary_derived: + query_primary_parens opt_for_system_time_clause table_alias_clause { - LEX *lex= Lex; - if (unlikely($1->init_nested_join(lex->thd))) - MYSQL_YYABORT; + if (!($$= Lex->parsed_derived_select($1, $2, $3))) + YYABORT; } - ; - -select_derived_init: + | '(' + query_expression + ')' opt_for_system_time_clause table_alias_clause { - LEX *lex= Lex; - - TABLE_LIST *embedding= lex->current_select->embedding; - $$= embedding && - !embedding->nested_join->join_list.elements; - /* return true if we are deeply nested */ + if (!($$= Lex->parsed_derived_unit($2, $4, $5))) + YYABORT; } ; @@ -12365,9 +12282,14 @@ table_alias: | '=' ; -opt_table_alias: +opt_table_alias_clause: /* empty */ { $$=0; } - | table_alias ident_table_alias + + | table_alias_clause { $$= $1; } + ; + +table_alias_clause: + table_alias ident_table_alias { $$= (LEX_CSTRING*) thd->memdup(&$2,sizeof(LEX_STRING)); if (unlikely($$ == NULL)) @@ -12463,7 +12385,7 @@ olap_opt: SQL-2003: GROUP BY ... CUBE(col1, col2, col3) */ LEX *lex=Lex; - if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)) + if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE)) my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH CUBE", "global union parameters")); lex->current_select->olap= CUBE_TYPE; @@ -12480,7 +12402,7 @@ olap_opt: SQL-2003: GROUP BY ... ROLLUP(col1, col2, col3) */ LEX *lex= Lex; - if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)) + if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE)) my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", "global union parameters")); lex->current_select->olap= ROLLUP_TYPE; @@ -12540,7 +12462,7 @@ opt_window_partition_clause: opt_window_order_clause: /* empty */ { } - | ORDER_SYM BY order_list + | ORDER_SYM BY order_list { Select->order_list= *($3); } ; opt_window_frame_clause: @@ -12664,70 +12586,35 @@ alter_order_item: opt_order_clause: /* empty */ + { $$= NULL; } | order_clause + { $$= $1; } ; order_clause: ORDER_SYM BY { - LEX *lex=Lex; - SELECT_LEX *sel= lex->current_select; - SELECT_LEX_UNIT *unit= sel-> master_unit(); - if (unlikely(sel->linkage != GLOBAL_OPTIONS_TYPE && - sel->olap != UNSPECIFIED_OLAP_TYPE && - (sel->linkage != UNION_TYPE || sel->braces))) - { - my_error(ER_WRONG_USAGE, MYF(0), - "CUBE/ROLLUP", "ORDER BY"); - MYSQL_YYABORT; - } - if (lex->sql_command != SQLCOM_ALTER_TABLE && - !unit->fake_select_lex) - { - /* - A query of the of the form (SELECT ...) ORDER BY order_list is - executed in the same way as the query - SELECT ... ORDER BY order_list - unless the SELECT construct contains ORDER BY or LIMIT clauses. - Otherwise we create a fake SELECT_LEX if it has not been - created yet. - */ - SELECT_LEX *first_sl= unit->first_select(); - if (unlikely(!unit->is_unit_op() && - (first_sl->order_list.elements || - first_sl->select_limit) && - unit->add_fake_select_lex(thd))) - MYSQL_YYABORT; - } - if (sel->master_unit()->is_unit_op() && !sel->braces) - { - /* - At this point we don't know yet whether this is the last - select in union or not, but we move ORDER BY to - fake_select_lex anyway. If there would be one more select - in union mysql_new_select will correctly throw error. - */ - DBUG_ASSERT(sel->master_unit()->fake_select_lex); - lex->current_select= sel->master_unit()->fake_select_lex; - } + thd->where= "ORDER clause"; } order_list { - + $$= $4; } ; order_list: order_list ',' order_ident order_dir { - if (unlikely(add_order_to_list(thd, $3,(bool) $4))) - MYSQL_YYABORT; - } + $$= $1; + if (add_to_list(thd, *$$, $3,(bool) $4)) + MYSQL_YYABORT; + } | order_ident order_dir { - if (unlikely(add_order_to_list(thd, $1,(bool) $2))) + $$= new (thd->mem_root) SQL_I_List<ORDER>(); + if (add_to_list(thd, *$$, $1, (bool) $2)) MYSQL_YYABORT; - } + } ; order_dir: @@ -12737,63 +12624,61 @@ order_dir: ; opt_limit_clause: - /* empty */ {} - | limit_clause {} + /* empty */ + { $$.empty(); } + | limit_clause + { $$= $1; } ; -limit_clause_init: - LIMIT - { - SELECT_LEX *sel= Select; - if (sel->master_unit()->is_unit_op() && !sel->braces) - { - /* Move LIMIT that belongs to UNION to fake_select_lex */ - Lex->current_select= sel->master_unit()->fake_select_lex; - DBUG_ASSERT(Select); - } - } - ; - limit_clause: - limit_clause_init limit_options + LIMIT limit_options { - SELECT_LEX *sel= Select; - if (!sel->select_limit->basic_const_item() || - sel->select_limit->val_int() > 0) + $$= $2; + if (!$$.select_limit->basic_const_item() || + $$.select_limit->val_int() > 0) Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); } - | limit_clause_init limit_options + | LIMIT limit_options ROWS_SYM EXAMINED_SYM limit_rows_option { + $$= $2; Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); } - | limit_clause_init ROWS_SYM EXAMINED_SYM limit_rows_option + | LIMIT ROWS_SYM EXAMINED_SYM limit_rows_option { + $$.select_limit= 0; + $$.offset_limit= 0; + $$.explicit_limit= 1; Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); } ; +opt_global_limit_clause: + opt_limit_clause + { + Select->explicit_limit= $1.explicit_limit; + Select->select_limit= $1.select_limit; + Select->offset_limit= $1.offset_limit; + } + limit_options: limit_option { - SELECT_LEX *sel= Select; - sel->select_limit= $1; - sel->offset_limit= 0; - sel->explicit_limit= 1; + $$.select_limit= $1; + $$.offset_limit= 0; + $$.explicit_limit= 1; } | limit_option ',' limit_option { - SELECT_LEX *sel= Select; - sel->select_limit= $3; - sel->offset_limit= $1; - sel->explicit_limit= 1; + $$.select_limit= $3; + $$.offset_limit= $1; + $$.explicit_limit= 1; } | limit_option OFFSET_SYM limit_option { - SELECT_LEX *sel= Select; - sel->select_limit= $1; - sel->offset_limit= $3; - sel->explicit_limit= 1; + $$.select_limit= $1; + $$.offset_limit= $3; + $$.explicit_limit= 1; } ; @@ -12856,6 +12741,77 @@ delete_limit_clause: | LIMIT limit_option ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; } ; +opt_order_limit_lock: + /* empty */ + { $$= NULL; } + | order_or_limit + { + $$= $1; + $$->lock.empty(); + } + | order_or_limit select_lock_type + { + $$= $1; + $$->lock= $2; + } + | select_lock_type + { + $$= new(thd->mem_root) Lex_order_limit_lock; + if (!$$) + YYABORT; + $$->order_list= NULL; + $$->limit.empty(); + $$->lock= $1; + } + ; +query_expression_tail: + opt_order_limit_lock + ; + +opt_procedure_or_into: + /* empty */ + { + $$.empty(); + } + | procedure_clause opt_select_lock_type + { + $$= $2; + } + | into opt_select_lock_type + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_DEPRECATED_SYNTAX, + ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX), + "<select expression> INTO <destination>;", + "'SELECT <select list> INTO <destination>" + " FROM...'"); + $$= $2; + } + ; + + +order_or_limit: + order_clause opt_limit_clause + { + $$= new(thd->mem_root) Lex_order_limit_lock; + if (!$$) + YYABORT; + $$->order_list= $1; + $$->limit= $2; + } + | limit_clause + { + Lex_order_limit_lock *op= $$= new(thd->mem_root) Lex_order_limit_lock; + if (!$$) + YYABORT; + op->order_list= NULL; + op->limit= $1; + $$->order_list= NULL; + $$->limit= $1; + } + ; + + opt_plus: /* empty */ | '+' @@ -12925,14 +12881,11 @@ bool: | TRUE_SYM { $$= 1; } | FALSE_SYM { $$= 0; } - procedure_clause: PROCEDURE_SYM ident /* Procedure name */ { LEX *lex=Lex; - DBUG_ASSERT(&lex->select_lex == lex->current_select); - lex->proc_list.elements=0; lex->proc_list.first=0; lex->proc_list.next= &lex->proc_list.first; @@ -12952,6 +12905,7 @@ procedure_clause: parameters are reduced. */ Lex->expr_allows_subselect= false; + Select->options|= OPTION_PROCEDURE_CLAUSE; } '(' procedure_list ')' { @@ -13035,6 +12989,7 @@ select_outvar: into: INTO into_destination + {} ; into_destination: @@ -13244,10 +13199,11 @@ table_list: table_name: table_ident { - if (unlikely(!Select->add_table_to_list(thd, $1, NULL, - TL_OPTION_UPDATING, - YYPS->m_lock_type, - YYPS->m_mdl_type))) + if (!thd->lex->current_select_or_default()-> + add_table_to_list(thd, $1, NULL, + TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type)) MYSQL_YYABORT; } ; @@ -13320,17 +13276,24 @@ insert: { LEX *lex= Lex; lex->sql_command= SQLCOM_INSERT; - lex->duplicates= DUP_ERROR; - mysql_init_select(lex); + lex->duplicates= DUP_ERROR; + if (Lex->main_select_push()) + MYSQL_YYABORT; + mysql_init_select(lex); + lex->current_select->parsing_place= BEFORE_OPT_LIST; } insert_lock_option opt_ignore insert2 { Select->set_lock_for_tables($3); - Lex->current_select= &Lex->select_lex; + Lex->current_select= Lex->first_select_lex(); } insert_field_spec opt_insert_update - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } ; replace: @@ -13339,15 +13302,22 @@ replace: LEX *lex=Lex; lex->sql_command = SQLCOM_REPLACE; lex->duplicates= DUP_REPLACE; - mysql_init_select(lex); + if (Lex->main_select_push()) + MYSQL_YYABORT; + mysql_init_select(lex); + lex->current_select->parsing_place= BEFORE_OPT_LIST; } replace_lock_option insert2 { Select->set_lock_for_tables($3); - Lex->current_select= &Lex->select_lex; + Lex->current_select= Lex->first_select_lex(); } insert_field_spec - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } ; insert_lock_option: @@ -13390,15 +13360,14 @@ insert_table: table_name_with_opt_use_partition { LEX *lex=Lex; - lex->field_list.empty(); + //lex->field_list.empty(); lex->many_values.empty(); lex->insert_list=0; }; insert_field_spec: insert_values {} - | '(' ')' insert_values {} - | '(' fields ')' insert_values {} + | insert_field_list insert_values {} | SET { LEX *lex=Lex; @@ -13406,20 +13375,33 @@ insert_field_spec: unlikely(lex->many_values.push_back(lex->insert_list, thd->mem_root))) MYSQL_YYABORT; + lex->current_select->parsing_place= NO_MATTER; } ident_eq_list ; +insert_field_list: + LEFT_PAREN_ALT opt_fields ')' + { + Lex->current_select->parsing_place= AFTER_LIST; + } + ; + +opt_fields: + /* empty */ + | fields + ; + fields: fields ',' insert_ident { Lex->field_list.push_back($3, thd->mem_root); } | insert_ident { Lex->field_list.push_back($1, thd->mem_root); } ; + + insert_values: - VALUES values_list {} - | VALUE_SYM values_list {} - | create_select_query_expression {} + create_select_query_expression {} ; values_list: @@ -13569,6 +13551,8 @@ update: UPDATE_SYM { LEX *lex= Lex; + if (Lex->main_select_push()) + MYSQL_YYABORT; mysql_init_select(lex); lex->sql_command= SQLCOM_UPDATE; lex->duplicates= DUP_ERROR; @@ -13577,13 +13561,14 @@ update: SET update_list { LEX *lex= Lex; - if (lex->select_lex.table_list.elements > 1) + if (lex->first_select_lex()->table_list.elements > 1) lex->sql_command= SQLCOM_UPDATE_MULTI; - else if (unlikely(lex->select_lex.get_table_list()->derived)) + else if (lex->first_select_lex()->get_table_list()->derived) { /* it is single table update and it is update of derived table */ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - lex->select_lex.get_table_list()->alias.str, "UPDATE"); + lex->first_select_lex()->get_table_list()->alias.str, + "UPDATE"); MYSQL_YYABORT; } /* @@ -13593,7 +13578,14 @@ update: */ Select->set_lock_for_tables($3); } - opt_where_clause opt_order_clause delete_limit_clause {} + opt_where_clause opt_order_clause delete_limit_clause + { + if ($10) + Select->order_list= *($10); + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } ; update_list: @@ -13640,9 +13632,11 @@ delete: mysql_init_select(lex); YYPS->m_lock_type= TL_WRITE_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_WRITE; + if (Lex->main_select_push()) + MYSQL_YYABORT; lex->ignore= 0; - lex->select_lex.init_order(); + lex->first_select_lex()->order_list.empty(); } delete_part2 ; @@ -13666,6 +13660,7 @@ delete_part2: } ; + delete_single_table: FROM table_ident opt_use_partition { @@ -13686,7 +13681,12 @@ single_multi: opt_where_clause opt_order_clause delete_limit_clause - opt_select_expressions {} + opt_select_expressions + { + if ($3) + Select->order_list= *($3); + Lex->pop_select(); //main select + } | table_wild_list { mysql_init_multi_delete(Lex); @@ -13697,6 +13697,9 @@ single_multi: { if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex))) MYSQL_YYABORT; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } | FROM table_alias_ref_list { @@ -13708,9 +13711,13 @@ single_multi: { if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex))) MYSQL_YYABORT; + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } ; + opt_select_expressions: /* empty */ | RETURNING_SYM select_item_list @@ -13776,9 +13783,9 @@ truncate: LEX* lex= Lex; lex->sql_command= SQLCOM_TRUNCATE; lex->alter_info.reset(); - lex->select_lex.options= 0; - lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; - lex->select_lex.init_order(); + lex->first_select_lex()->options= 0; + lex->sql_cache= LEX::SQL_CACHE_UNSPECIFIED; + lex->first_select_lex()->order_list.empty(); YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; } @@ -13870,6 +13877,8 @@ show: LEX *lex=Lex; lex->wild=0; lex->ident= null_clex_str; + if (Lex->main_select_push()) + MYSQL_YYABORT; mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LIST; lex->create_info.init(); @@ -13877,6 +13886,7 @@ show: show_param { Select->parsing_place= NO_MATTER; + Lex->pop_select(); //main select } ; @@ -13892,40 +13902,40 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLES; - lex->select_lex.db= $3; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES))) + lex->first_select_lex()->db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)) MYSQL_YYABORT; } | opt_full TRIGGERS_SYM opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TRIGGERS; - lex->select_lex.db= $3; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TRIGGERS))) + lex->first_select_lex()->db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)) MYSQL_YYABORT; } | EVENTS_SYM opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_EVENTS; - lex->select_lex.db= $2; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_EVENTS))) + lex->first_select_lex()->db= $2; + if (prepare_schema_table(thd, lex, 0, SCH_EVENTS)) MYSQL_YYABORT; } | TABLE_SYM STATUS_SYM opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; - lex->select_lex.db= $3; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLES))) + lex->first_select_lex()->db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_TABLES)) MYSQL_YYABORT; } | OPEN_SYM TABLES opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; - lex->select_lex.db= $3; - if (unlikely(prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES))) + lex->first_select_lex()->db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)) MYSQL_YYABORT; } | PLUGINS_SYM @@ -13974,12 +13984,13 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS; } - opt_limit_clause + opt_global_limit_clause | RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS; - } opt_limit_clause + } + opt_global_limit_clause | keys_or_index from_or_in table_ident opt_db opt_where_clause { LEX *lex= Lex; @@ -14021,13 +14032,13 @@ show_param: LEX_CSTRING var= {STRING_WITH_LEN("error_count")}; (void) create_select_for_variable(thd, &var); } - | WARNINGS opt_limit_clause + | WARNINGS opt_global_limit_clause { Lex->sql_command = SQLCOM_SHOW_WARNS;} - | ERRORS opt_limit_clause + | ERRORS opt_global_limit_clause { Lex->sql_command = SQLCOM_SHOW_ERRORS;} | PROFILES_SYM { Lex->sql_command = SQLCOM_SHOW_PROFILES; } - | PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause + | PROFILE_SYM opt_profile_defs opt_profile_args opt_global_limit_clause { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_PROFILE; @@ -14089,7 +14100,7 @@ show_param: { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL,0))) + if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL,0)) MYSQL_YYABORT; lex->create_info.storage_media= HA_SM_DEFAULT; } @@ -14097,7 +14108,7 @@ show_param: { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL, 0))) + if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0)) MYSQL_YYABORT; lex->table_type= TABLE_TYPE_VIEW; } @@ -14105,7 +14116,7 @@ show_param: { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; - if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL, 0))) + if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0)) MYSQL_YYABORT; lex->table_type= TABLE_TYPE_SEQUENCE; } @@ -14322,7 +14333,7 @@ describe: mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LIST; lex->sql_command= SQLCOM_SHOW_FIELDS; - lex->select_lex.db= null_clex_str; + lex->first_select_lex()->db= null_clex_str; lex->verbose= 0; if (unlikely(prepare_schema_table(thd, lex, $2, SCH_COLUMNS))) MYSQL_YYABORT; @@ -14336,12 +14347,13 @@ describe: explainable_command { LEX *lex=Lex; - lex->select_lex.options|= SELECT_DESCRIBE; + lex->first_select_lex()->options|= SELECT_DESCRIBE; } ; explainable_command: select + | select_into | insert | replace | update @@ -14362,6 +14374,8 @@ analyze_stmt_command: opt_extended_describe: EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; } + | EXTENDED_SYM ALL + { Lex->describe|= DESCRIBE_EXTENDED | DESCRIBE_EXTENDED2; } | PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; } | opt_format_json {} ; @@ -14404,8 +14418,7 @@ flush: lex->type= 0; lex->no_write_to_binlog= $2; } - flush_options - {} + flush_options {} ; flush_options: @@ -14422,6 +14435,7 @@ flush_options: opt_table_list opt_flush_lock {} | flush_options_list + {} ; opt_flush_lock: @@ -14677,7 +14691,7 @@ use: { LEX *lex=Lex; lex->sql_command=SQLCOM_CHANGE_DB; - lex->select_lex.db= $2; + lex->first_select_lex()->db= $2; } ; @@ -14694,6 +14708,8 @@ load: $2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML"); MYSQL_YYABORT; } + if (Lex->main_select_push()) + MYSQL_YYABORT; } load_data_lock opt_local INFILE TEXT_STRING_filesystem { @@ -14723,7 +14739,11 @@ load: opt_xml_rows_identified_by opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec opt_load_data_set_spec - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } ; data_or_xml: @@ -14921,11 +14941,6 @@ hex_or_bin_String: $1.length); if (unlikely(tmp == NULL)) MYSQL_YYABORT; - /* - it is OK only emulate fix_fields, because we need only - value of constant - */ - tmp->quick_fix_field(); $$= tmp->val_str((String*) 0); } | HEX_STRING @@ -14934,7 +14949,6 @@ hex_or_bin_String: $1.length); if (unlikely(tmp == NULL)) MYSQL_YYABORT; - tmp->quick_fix_field(); $$= tmp->val_str((String*) 0); } | BIN_NUM @@ -14947,7 +14961,6 @@ hex_or_bin_String: it is OK only emulate fix_fields, because we need only value of constant */ - tmp->quick_fix_field(); $$= tmp->val_str((String*) 0); } ; @@ -15095,26 +15108,23 @@ NUM_literal: temporal_literal: DATE_SYM TEXT_STRING { - if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length, - YYCSCL, - MYSQL_TYPE_DATE, - true)))) + if (unlikely(!($$= type_handler_newdate.create_literal_item(thd, + $2.str, $2.length, + YYCSCL, true)))) MYSQL_YYABORT; } | TIME_SYM TEXT_STRING { - if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length, - YYCSCL, - MYSQL_TYPE_TIME, - true)))) + if (unlikely(!($$= type_handler_time2.create_literal_item(thd, + $2.str, $2.length, + YYCSCL, true)))) MYSQL_YYABORT; } | TIMESTAMP TEXT_STRING { - if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length, - YYCSCL, - MYSQL_TYPE_DATETIME, - true)))) + if (unlikely(!($$= type_handler_datetime2.create_literal_item(thd, + $2.str, $2.length, + YYCSCL, true)))) MYSQL_YYABORT; } ; @@ -15130,17 +15140,21 @@ opt_with_clause: with_clause: - WITH opt_recursive + WITH opt_recursive { + LEX *lex= Lex; With_clause *with_clause= new With_clause($2, Lex->curr_with_clause); if (unlikely(with_clause == NULL)) MYSQL_YYABORT; - Lex->derived_tables|= DERIVED_WITH; - Lex->curr_with_clause= with_clause; + lex->derived_tables|= DERIVED_WITH; + lex->curr_with_clause= with_clause; with_clause->add_to_list(Lex->with_clauses_list_last_next); + if (lex->current_select && + lex->current_select->parsing_place == BEFORE_OPT_LIST) + lex->current_select->parsing_place= NO_MATTER; } - with_list + with_list { $$= Lex->curr_with_clause; Lex->curr_with_clause= Lex->curr_with_clause->pop(); @@ -15169,15 +15183,14 @@ with_list_element: MYSQL_YYABORT; Lex->with_column_list.empty(); } - AS '(' remember_tok_start subselect remember_tok_end ')' + AS '(' remember_tok_start query_expression remember_tok_end ')' { LEX *lex= thd->lex; const char *query_start= lex->sphead ? lex->sphead->m_tmp_query : thd->query(); char *spec_start= $6 + 1; - With_element *elem= new With_element($1, *$2, $7->master_unit()); - if (unlikely(elem == NULL) || - unlikely(Lex->curr_with_clause->add_with_element(elem))) + With_element *elem= new With_element($1, *$2, $7); + if (elem == NULL || Lex->curr_with_clause->add_with_element(elem)) MYSQL_YYABORT; if (elem->set_unparsed_spec(thd, spec_start, $8, spec_start - query_start)) @@ -16149,14 +16162,22 @@ set: SET { LEX *lex=Lex; + if (lex->main_select_push()) + MYSQL_YYABORT; lex->set_stmt_init(); lex->var_list.empty(); sp_create_assignment_lex(thd, yychar == YYEMPTY); } start_option_value_list - {} + { + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } | SET STATEMENT_SYM { + if (Lex->main_select_push()) + MYSQL_YYABORT; Lex->set_stmt_init(); } set_stmt_option_value_following_option_type_list @@ -16166,6 +16187,9 @@ set: my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "SET STATEMENT")); lex->stmt_var_list= lex->var_list; lex->var_list.empty(); + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; } FOR_SYM verb_clause {} @@ -16557,14 +16581,14 @@ opt_for_user: ; text_or_password: - TEXT_STRING { Lex->definer->pwhash= $1;} + TEXT_STRING { Lex->definer->auth= $1;} | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; } | OLD_PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; - Lex->definer->pwhash.str= Item_func_password::alloc(thd, + Lex->definer->auth.str= Item_func_password::alloc(thd, $3.str, $3.length, Item_func_password::OLD); - Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + Lex->definer->auth.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; } ; @@ -16634,7 +16658,7 @@ table_lock_list: ; table_lock: - table_ident opt_table_alias lock_option + table_ident opt_table_alias_clause lock_option { thr_lock_type lock_type= (thr_lock_type) $3; bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE); @@ -16679,27 +16703,36 @@ unlock: */ handler: - HANDLER_SYM table_ident OPEN_SYM opt_table_alias + HANDLER_SYM + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + } + handler_tail + { + Lex->pop_select(); //main select + } + +handler_tail: + table_ident OPEN_SYM opt_table_alias_clause { LEX *lex= Lex; if (unlikely(lex->sphead)) my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); lex->sql_command = SQLCOM_HA_OPEN; - if (unlikely(!lex->current_select->add_table_to_list(thd, $2, $4, - 0))) + if (!lex->current_select->add_table_to_list(thd, $1, $3, 0)) MYSQL_YYABORT; } - | HANDLER_SYM table_ident_nodb CLOSE_SYM + | table_ident_nodb CLOSE_SYM { LEX *lex= Lex; if (unlikely(lex->sphead)) my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); lex->sql_command = SQLCOM_HA_CLOSE; - if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0, - 0))) + if (!lex->current_select->add_table_to_list(thd, $1, 0, 0)) MYSQL_YYABORT; } - | HANDLER_SYM table_ident_nodb READ_SYM + | table_ident_nodb READ_SYM { LEX *lex=Lex; if (unlikely(lex->sphead)) @@ -16713,15 +16746,24 @@ handler: lex->current_select->select_limit= one; lex->current_select->offset_limit= 0; lex->limit_rows_examined= 0; - if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0, - 0))) + if (!lex->current_select->add_table_to_list(thd, $1, 0, 0)) MYSQL_YYABORT; } - handler_read_or_scan opt_where_clause opt_limit_clause + handler_read_or_scan opt_where_clause opt_global_limit_clause { - Lex->expr_allows_subselect= TRUE; + LEX *lex=Lex; + lex->expr_allows_subselect= TRUE; + if (!lex->current_select->explicit_limit) + { + Item *one= new (thd->mem_root) Item_int(thd, (int32) 1); + if (one == NULL) + MYSQL_YYABORT; + lex->current_select->select_limit= one; + lex->current_select->offset_limit= 0; + lex->limit_rows_examined= 0; + } /* Stored functions are not supported for HANDLER READ. */ - if (unlikely(Lex->uses_stored_routines())) + if (lex->uses_stored_routines()) { my_error(ER_NOT_SUPPORTED_YET, MYF(0), "stored functions in HANDLER ... READ"); @@ -17130,13 +17172,11 @@ grant_user: { $$= $1; $1->pwtext= $4; - if (unlikely(Lex->sql_command == SQLCOM_REVOKE)) - MYSQL_YYABORT; } | user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING { $$= $1; - $1->pwhash= $5; + $1->auth= $5; } | user IDENTIFIED_SYM via_or_with ident_or_text { @@ -17144,12 +17184,20 @@ grant_user: $1->plugin= $4; $1->auth= empty_clex_str; } - | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as + TEXT_STRING_sys { $$= $1; $1->plugin= $4; $1->auth= $6; } + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as + PASSWORD_SYM '(' TEXT_STRING ')' + { + $$= $1; + $1->plugin= $4; + $1->pwtext= $8; + } | user_or_role { $$= $1; } ; @@ -17373,83 +17421,16 @@ release: */ unit_type_decl: - UNION_SYM - { $$= UNION_TYPE; } + UNION_SYM union_option + { $$.unit_type= UNION_TYPE; $$.distinct= $2; } | INTERSECT_SYM - { $$= INTERSECT_TYPE; } + { $$.unit_type= INTERSECT_TYPE; $$.distinct= 1; } | EXCEPT_SYM - { $$= EXCEPT_TYPE; } - - -union_clause: - /* empty */ {} - | union_list - ; - -union_list: - unit_type_decl union_option - { - if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE))) - MYSQL_YYABORT; - } - union_list_part2 - { - /* - Remove from the name resolution context stack the context of the - last select in the union. - */ - Lex->pop_context(); - } - ; - -union_list_view: - unit_type_decl union_option - { - if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE))) - MYSQL_YYABORT; - } - query_expression_body_view - { - Lex->pop_context(); - } - ; - -union_order_or_limit: - { - LEX *lex= thd->lex; - DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE); - SELECT_LEX *sel= lex->current_select; - SELECT_LEX_UNIT *unit= sel->master_unit(); - SELECT_LEX *fake= unit->fake_select_lex; - if (fake) - { - fake->no_table_names_allowed= 1; - lex->current_select= fake; - } - thd->where= "global ORDER clause"; - } - order_or_limit - { - thd->lex->current_select->no_table_names_allowed= 0; - thd->where= ""; - } - ; - -order_or_limit: - order_clause opt_limit_clause - | limit_clause - ; + { $$.unit_type= EXCEPT_TYPE; $$.distinct= 1; } /* Start a UNION, for non-top level query expressions. */ -union_head_non_top: - unit_type_decl union_option - { - if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, FALSE))) - MYSQL_YYABORT; - } - ; union_option: /* empty */ { $$=1; } @@ -17457,128 +17438,10 @@ union_option: | ALL { $$=0; } ; -simple_table: - query_specification { $$= $1; } - | table_value_constructor { $$= $1; } - ; - -table_value_constructor: - VALUES - { - Lex->tvc_start(); - } - values_list - { - $$= Lex->current_select; - if (Lex->tvc_finalize()) - MYSQL_YYABORT; - } - ; - -/* - Corresponds to the SQL Standard - <query specification> ::= - SELECT [ <set quantifier> ] <select list> <table expression> - - Notes: - - We allow more options in addition to <set quantifier> - - <table expression> is optional in MariaDB -*/ -query_specification: - SELECT_SYM select_init2_derived opt_table_expression - { - $$= Lex->current_select->master_unit()->first_select(); - } - ; - -query_term_union_not_ready: - simple_table order_or_limit opt_select_lock_type { $$= $1; } - | '(' select_paren_derived ')' union_order_or_limit { $$= $2; } - ; - -query_term_union_ready: - simple_table opt_select_lock_type { $$= $1; } - | '(' select_paren_derived ')' { $$= $2; } - ; - -query_expression_body: - query_term_union_not_ready { $$= $1; } - | query_term_union_ready { $$= $1; } - | query_term_union_ready union_list_derived { $$= $1; } - ; - -/* Corresponds to <query expression> in the SQL:2003 standard. */ -subselect: - subselect_start opt_with_clause query_expression_body subselect_end - { - $3->set_with_clause($2); - $$= $3; - } - ; - -subselect_start: - { - LEX *lex=Lex; - if (unlikely(!lex->expr_allows_subselect || - lex->sql_command == (int)SQLCOM_PURGE)) - { - thd->parse_error(); - MYSQL_YYABORT; - } - /* - we are making a "derived table" for the parenthesis - as we need to have a lex level to fit the union - after the parenthesis, e.g. - (SELECT .. ) UNION ... becomes - SELECT * FROM ((SELECT ...) UNION ...) - */ - if (unlikely(mysql_new_select(Lex, 1, NULL))) - MYSQL_YYABORT; - } - ; - -subselect_end: - { - LEX *lex=Lex; - - lex->check_automatic_up(UNSPECIFIED_TYPE); - lex->pop_context(); - SELECT_LEX *child= lex->current_select; - lex->current_select = lex->current_select->return_after_parsing(); - lex->nest_level--; - lex->current_select->n_child_sum_items += child->n_sum_items; - /* - A subselect can add fields to an outer select. Reserve space for - them. - */ - lex->current_select->select_n_where_fields+= - child->select_n_where_fields; - - /* - Aggregate functions in having clause may add fields to an outer - select. Count them also. - */ - lex->current_select->select_n_having_items+= - child->select_n_having_items; - } - ; - -opt_query_expression_options: - /* empty */ - | query_expression_option_list - ; - -query_expression_option_list: - query_expression_option_list query_expression_option - | query_expression_option - ; - query_expression_option: STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } | HIGH_PRIORITY { - if (unlikely(Lex->check_simple_select(&$1))) - MYSQL_YYABORT; YYPS->m_lock_type= TL_READ_HIGH_PRIORITY; YYPS->m_mdl_type= MDL_SHARED_READ; Select->options|= SELECT_HIGH_PRIORITY; @@ -17587,18 +17450,8 @@ query_expression_option: | UNIQUE_SYM { Select->options|= SELECT_DISTINCT; } | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } - | SQL_BUFFER_RESULT - { - if (unlikely(Lex->check_simple_select(&$1))) - MYSQL_YYABORT; - Select->options|= OPTION_BUFFER_RESULT; - } - | SQL_CALC_FOUND_ROWS - { - if (unlikely(Lex->check_simple_select(&$1))) - MYSQL_YYABORT; - Select->options|= OPTION_FOUND_ROWS; - } + | SQL_BUFFER_RESULT { Select->options|= OPTION_BUFFER_RESULT; } + | SQL_CALC_FOUND_ROWS { Select->options|= OPTION_FOUND_ROWS; } | ALL { Select->options|= SELECT_ALL; } ; @@ -17686,35 +17539,14 @@ view_select: lex->parsing_options.allows_variable= FALSE; lex->create_view->select.str= (char *) YYLIP->get_cpp_ptr(); } - opt_with_clause query_expression_body_view view_check_option + query_expression + view_check_option { - LEX *lex= Lex; - size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str; - void *create_view_select= thd->memdup(lex->create_view->select.str, len); - lex->create_view->select.length= len; - lex->create_view->select.str= (char *) create_view_select; - trim_whitespace(thd->charset(), - &lex->create_view->select); - lex->create_view->check= $4; - lex->parsing_options.allows_variable= TRUE; - lex->current_select->set_with_clause($2); + if (Lex->parsed_create_view($2, $3)) + MYSQL_YYABORT; } ; -/* - SQL Standard <query expression body> for VIEWs. - Does not include INTO and PROCEDURE clauses. -*/ -query_expression_body_view: - SELECT_SYM select_options_and_item_list select_init3_view - | table_value_constructor - | table_value_constructor union_order_or_limit - | table_value_constructor union_list_view - | '(' select_paren_view ')' - | '(' select_paren_view ')' union_order_or_limit - | '(' select_paren_view ')' union_list_view - ; - view_check_option: /* empty */ { $$= VIEW_CHECK_NONE; } | WITH CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; } @@ -17815,11 +17647,10 @@ trigger_tail: sp_proc_stmt alternatives are not saving/restoring LEX, so lex->query_tables can be wiped out. */ - if (unlikely(!lex->select_lex. - add_table_to_list(thd, $10, (LEX_CSTRING*) 0, - TL_OPTION_UPDATING, - TL_READ_NO_INSERT, - MDL_SHARED_NO_WRITE))) + if (!lex->first_select_lex()-> + add_table_to_list(thd, $10, (LEX_CSTRING*) 0, + TL_OPTION_UPDATING, TL_READ_NO_INSERT, + MDL_SHARED_NO_WRITE)) MYSQL_YYABORT; } ; diff --git a/sql/structs.h b/sql/structs.h index d8b95a3509a..9ff52bccb40 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -227,12 +227,11 @@ struct AUTHID struct LEX_USER: public AUTHID { - LEX_CSTRING plugin, auth; - LEX_CSTRING pwtext, pwhash; + LEX_CSTRING plugin, auth, pwtext; void reset_auth() { - pwtext.length= pwhash.length= plugin.length= auth.length= 0; - pwtext.str= pwhash.str= 0; + pwtext.length= plugin.length= auth.length= 0; + pwtext.str= 0; plugin.str= auth.str= ""; } }; @@ -758,6 +757,43 @@ public: }; +class st_select_lex; + +class Lex_select_lock +{ +public: + struct + { + uint defined_lock:1; + uint update_lock:1; + uint defined_timeout:1; + }; + ulong timeout; + + + void empty() + { + defined_lock= update_lock= defined_timeout= FALSE; + timeout= 0; + } + void set_to(st_select_lex *sel); +}; + +class Lex_select_limit +{ +public: + bool explicit_limit; + Item *select_limit, *offset_limit; + + void empty() + { + explicit_limit= FALSE; + select_limit= offset_limit= NULL; + } +}; + +struct st_order; + class Load_data_param { protected: @@ -794,4 +830,20 @@ public: }; +class Timeval: public timeval +{ +public: + Timeval(my_time_t sec, ulong usec) + { + tv_sec= sec; + tv_usec= usec; + } + Timeval &trunc(uint dec) + { + my_timeval_trunc(this, dec); + return *this; + } +}; + + #endif /* STRUCTS_INCLUDED */ diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 6d4c135683a..6d2dbbf646e 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2496,6 +2496,7 @@ export const char *optimizer_switch_names[]= "orderby_uses_equalities", "condition_pushdown_for_derived", "split_materialized", + "condition_pushdown_for_subquery", "default", NullS }; @@ -2720,17 +2721,6 @@ static Sys_var_ulong Sys_query_prealloc_size( BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_thd_mem_root)); -#ifdef HAVE_SMEM -static Sys_var_mybool Sys_shared_memory( - "shared_memory", "Enable the shared memory", - READ_ONLY GLOBAL_VAR(opt_enable_shared_memory), CMD_LINE(OPT_ARG), - DEFAULT(FALSE)); - -static Sys_var_charptr Sys_shared_memory_base_name( - "shared_memory_base_name", "Base name of shared memory", - READ_ONLY GLOBAL_VAR(shared_memory_base_name), CMD_LINE(REQUIRED_ARG), - IN_FS_CHARSET, DEFAULT(0)); -#endif // this has to be NO_CMD_LINE as the command-line option has a different name static Sys_var_mybool Sys_skip_external_locking( @@ -4052,6 +4042,16 @@ static bool fix_sql_log_bin_after_update(sys_var *self, THD *thd, return FALSE; } +static bool check_session_only_variable(sys_var *self, THD *,set_var *var) +{ + if (unlikely(var->type == OPT_GLOBAL)) + { + my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), self->name.str, "SESSION"); + return true; + } + return false; +} + /** This function checks if the sql_log_bin can be changed, what is possible if: @@ -4067,20 +4067,17 @@ static bool fix_sql_log_bin_after_update(sys_var *self, THD *thd, static bool check_sql_log_bin(sys_var *self, THD *thd, set_var *var) { if (check_has_super(self, thd, var)) - return TRUE; + return true; - if (unlikely(var->type == OPT_GLOBAL)) - { - my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), self->name.str, "SESSION"); - return TRUE; - } + if (check_session_only_variable(self, thd, var)) + return true; if (unlikely(error_if_in_trans_or_substatement(thd, ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN, ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN))) - return TRUE; + return true; - return FALSE; + return false; } static Sys_var_mybool Sys_log_binlog( @@ -5592,6 +5589,27 @@ static Sys_var_int Sys_keepalive_probes( BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL)); + +static bool update_tcp_nodelay(sys_var *self, THD *thd, + enum_var_type type) +{ + DBUG_ASSERT(thd); + + Vio *vio = thd->net.vio; + if (vio) + return (MY_TEST(vio_nodelay(vio, thd->variables.tcp_nodelay))); + + return false; +} + +static Sys_var_mybool Sys_tcp_nodelay( + "tcp_nodelay", + "Set option TCP_NODELAY (disable Nagle's algorithm) on socket", + SESSION_VAR(tcp_nodelay), CMD_LINE(OPT_ARG), + DEFAULT(TRUE),NO_MUTEX_GUARD, NOT_IN_BINLOG, + ON_CHECK(check_session_only_variable), + ON_UPDATE(update_tcp_nodelay)); + static Sys_var_charptr Sys_ignore_db_dirs( "ignore_db_dirs", "Specifies a directory to add to the ignore list when collecting " diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index 373df354268..dbc3565e202 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -2659,7 +2659,7 @@ public: if (!Sys_var_enum::do_check(thd, var)) return false; MYSQL_TIME ltime; - bool res= var->value->get_date(<ime, 0); + bool res= var->value->get_date(thd, <ime, date_mode_t(0)); if (!res) { var->save_result.ulonglong_value= SYSTEM_TIME_AS_OF; @@ -2676,7 +2676,7 @@ private: { if (var->value) { - res= var->value->get_date(&out.ltime, 0); + res= var->value->get_date(current_thd, &out.ltime, date_mode_t(0)); } else // set DEFAULT from global var { diff --git a/sql/table.cc b/sql/table.cc index ccb580badf3..6c3e350f07d 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -44,6 +44,7 @@ #include "sql_cte.h" #include "ha_sequence.h" #include "sql_show.h" +#include <atomic> /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -79,7 +80,7 @@ LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")}; */ static LEX_CSTRING parse_vcol_keyword= { STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; -static int64 last_table_id; +static std::atomic<ulong> last_table_id; /* Functions defined in this file */ @@ -344,8 +345,8 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, */ do { - share->table_map_id=(ulong) my_atomic_add64_explicit(&last_table_id, 1, - MY_MEMORY_ORDER_RELAXED); + share->table_map_id= + last_table_id.fetch_add(1, std::memory_order_relaxed); } while (unlikely(share->table_map_id == ~0UL)); } DBUG_RETURN(share); @@ -917,6 +918,54 @@ static uint upgrade_collation(ulong mysql_version, uint cs_number) } +void Column_definition_attributes::frm_pack_basic(uchar *buff) const +{ + int2store(buff + 3, length); + int2store(buff + 8, pack_flag); + buff[10]= (uchar) unireg_check; +} + + +void Column_definition_attributes::frm_unpack_basic(const uchar *buff) +{ + length= uint2korr(buff + 3); + pack_flag= uint2korr(buff + 8); + unireg_check= (Field::utype) MTYP_TYPENR((uint) buff[10]); +} + + +void Column_definition_attributes::frm_pack_charset(uchar *buff) const +{ + buff[11]= (uchar) (charset->number >> 8); + buff[14]= (uchar) charset->number; +} + + +bool Column_definition_attributes::frm_unpack_charset(TABLE_SHARE *share, + const uchar *buff) +{ + uint cs_org= buff[14] + (((uint) buff[11]) << 8); + uint cs_new= upgrade_collation(share->mysql_version, cs_org); + if (cs_org != cs_new) + share->incompatible_version|= HA_CREATE_USED_CHARSET; + if (cs_new && !(charset= get_charset(cs_new, MYF(0)))) + { + const char *csname= get_charset_name((uint) cs_new); + char tmp[10]; + if (!csname || csname[0] =='?') + { + my_snprintf(tmp, sizeof(tmp), "#%u", cs_new); + csname= tmp; + } + my_printf_error(ER_UNKNOWN_COLLATION, + "Unknown collation '%s' in table '%-.64s' definition", + MYF(0), csname, share->table_name.str); + return true; + } + return false; +} + + /* In MySQL 5.7 the null bits for not stored virtual fields are last. Calculate the position for these bits @@ -1147,6 +1196,38 @@ end: DBUG_RETURN(res); } + +static const Type_handler *old_frm_type_handler(uint pack_flag, + uint interval_nr) +{ + enum_field_types field_type= (enum_field_types) f_packtype(pack_flag); + DBUG_ASSERT(field_type < 16); + + if (!f_is_alpha(pack_flag)) + return Type_handler::get_handler_by_real_type(field_type); + + if (!f_is_packed(pack_flag)) + { + if (field_type == MYSQL_TYPE_DECIMAL) // 3.23 or 4.0 string + return &type_handler_string; + if (field_type == MYSQL_TYPE_VARCHAR) // Since mysql-5.0 + return &type_handler_varchar; + return NULL; // Error (bad frm?) + } + + if (f_is_blob(pack_flag)) + return &type_handler_blob; // QQ: exact type?? + + if (interval_nr) + { + if (f_is_enum(pack_flag)) + return &type_handler_enum; + return &type_handler_set; + } + return Type_handler::get_handler_by_real_type(field_type); +} + + /** Read data from a binary .frm file image into a TABLE_SHARE @@ -1193,8 +1274,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, size_t UNINIT_VAR(options_len); uchar *vcol_screen_pos; const uchar *options= 0; - size_t UNINIT_VAR(gis_options_len); - const uchar *gis_options= 0; + LEX_CUSTRING gis_options= { NULL, 0}; KEY first_keyinfo; uint len; uint ext_key_parts= 0; @@ -1290,10 +1370,10 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, case EXTRA2_GIS: #ifdef HAVE_SPATIAL { - if (gis_options) + if (gis_options.str) goto err; - gis_options= extra2; - gis_options_len= length; + gis_options.str= extra2; + gis_options.length= length; } #endif /*HAVE_SPATIAL*/ break; @@ -1784,83 +1864,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { - uint pack_flag, interval_nr, unireg_type, recpos, field_length; - uint vcol_info_length=0; - uint vcol_expr_length=0; - enum_field_types field_type; - CHARSET_INFO *charset=NULL; - Field::geometry_type geom_type= Field::GEOM_GEOMETRY; + uint interval_nr= 0, recpos; LEX_CSTRING comment; LEX_CSTRING name; Virtual_column_info *vcol_info= 0; - uint gis_length, gis_decimals, srid= 0; - Field::utype unireg_check; const Type_handler *handler; uint32 flags= 0; + Column_definition_attributes attr; if (new_frm_ver >= 3) { /* new frm file in 4.1 */ - field_length= uint2korr(strpos+3); recpos= uint3korr(strpos+5); - pack_flag= uint2korr(strpos+8); - unireg_type= (uint) strpos[10]; - interval_nr= (uint) strpos[12]; uint comment_length=uint2korr(strpos+15); - field_type=(enum_field_types) (uint) strpos[13]; - - /* charset and geometry_type share the same byte in frm */ - if (field_type == MYSQL_TYPE_GEOMETRY) - { -#ifdef HAVE_SPATIAL - uint gis_opt_read; - Field_geom::storage_type st_type; - geom_type= (Field::geometry_type) strpos[14]; - charset= &my_charset_bin; - gis_opt_read= gis_field_options_read(gis_options, gis_options_len, - &st_type, &gis_length, &gis_decimals, &srid); - gis_options+= gis_opt_read; - gis_options_len-= gis_opt_read; -#else - goto err; -#endif - } - else - { - uint cs_org= strpos[14] + (((uint) strpos[11]) << 8); - uint cs_new= upgrade_collation(share->mysql_version, cs_org); - if (cs_org != cs_new) - share->incompatible_version|= HA_CREATE_USED_CHARSET; - if (!cs_new) - charset= &my_charset_bin; - else if (!(charset= get_charset(cs_new, MYF(0)))) - { - const char *csname= get_charset_name((uint) cs_new); - char tmp[10]; - if (!csname || csname[0] =='?') - { - my_snprintf(tmp, sizeof(tmp), "#%u", cs_new); - csname= tmp; - } - my_printf_error(ER_UNKNOWN_COLLATION, - "Unknown collation '%s' in table '%-.64s' definition", - MYF(0), csname, share->table_name.str); - goto err; - } - } - - if ((uchar)field_type == (uchar)MYSQL_TYPE_VIRTUAL) - { - if (!interval_nr) // Expect non-null expression - goto err; - /* - MariaDB version 10.0 version. - The interval_id byte in the .frm file stores the length of the - expression statement for a virtual column. - */ - vcol_info_length= interval_nr; - interval_nr= 0; - } if (!comment_length) { @@ -1874,32 +1890,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, comment_pos+= comment_length; } - if (unireg_type & MYSQL57_GENERATED_FIELD) + if ((uchar) strpos[13] == (uchar) MYSQL_TYPE_VIRTUAL) { - unireg_type&= MYSQL57_GENERATED_FIELD; - /* - MySQL 5.7 generated fields - - byte 1 = 1 - byte 2,3 = expr length - byte 4 = stored_in_db - byte 5.. = expr + MariaDB version 10.0 version. + The interval_id byte in the .frm file stores the length of the + expression statement for a virtual column. */ - if ((uint)(vcol_screen_pos)[0] != 1) + uint vcol_info_length= (uint) strpos[12]; + + if (!vcol_info_length) // Expect non-null expression goto err; - vcol_info= new (&share->mem_root) Virtual_column_info(); - vcol_info_length= uint2korr(vcol_screen_pos + 1); - DBUG_ASSERT(vcol_info_length); - vcol_info->stored_in_db= vcol_screen_pos[3]; - vcol_info->utf8= 0; - vcol_screen_pos+= vcol_info_length + MYSQL57_GCOL_HEADER_SIZE;; - share->virtual_fields++; - vcol_info_length= 0; - } - if (vcol_info_length) - { + attr.frm_unpack_basic(strpos); + if (attr.frm_unpack_charset(share, strpos)) + goto err; /* Old virtual field information before 10.2 @@ -1913,7 +1918,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, vcol_info= new (&share->mem_root) Virtual_column_info(); bool opt_interval_id= (uint)vcol_screen_pos[0] == 2; - field_type= (enum_field_types) (uchar) vcol_screen_pos[1]; + enum_field_types ftype= (enum_field_types) (uchar) vcol_screen_pos[1]; + if (!(handler= Type_handler::get_handler_by_real_type(ftype))) + goto err; if (opt_interval_id) interval_nr= (uint)vcol_screen_pos[3]; else if ((uint)vcol_screen_pos[0] != 1) @@ -1921,26 +1928,63 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, bool stored= vcol_screen_pos[2] & 1; vcol_info->stored_in_db= stored; vcol_info->set_vcol_type(stored ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL); - vcol_expr_length= vcol_info_length - - (uint)(FRM_VCOL_OLD_HEADER_SIZE(opt_interval_id)); + uint vcol_expr_length= vcol_info_length - + (uint)(FRM_VCOL_OLD_HEADER_SIZE(opt_interval_id)); vcol_info->utf8= 0; // before 10.2.1 the charset was unknown int2store(vcol_screen_pos+1, vcol_expr_length); // for parse_vcol_defs() vcol_screen_pos+= vcol_info_length; share->virtual_fields++; } + else + { + interval_nr= (uint) strpos[12]; + enum_field_types field_type= (enum_field_types) strpos[13]; + if (!(handler= Type_handler::get_handler_by_real_type(field_type))) + goto err; // Not supported field type + if (handler->Column_definition_attributes_frm_unpack(&attr, share, + strpos, + &gis_options)) + goto err; + } + + if (((uint) strpos[10]) & MYSQL57_GENERATED_FIELD) + { + attr.unireg_check= Field::NONE; + + /* + MySQL 5.7 generated fields + + byte 1 = 1 + byte 2,3 = expr length + byte 4 = stored_in_db + byte 5.. = expr + */ + if ((uint)(vcol_screen_pos)[0] != 1) + goto err; + vcol_info= new (&share->mem_root) Virtual_column_info(); + uint vcol_info_length= uint2korr(vcol_screen_pos + 1); + DBUG_ASSERT(vcol_info_length); + vcol_info->stored_in_db= vcol_screen_pos[3]; + vcol_info->utf8= 0; + vcol_screen_pos+= vcol_info_length + MYSQL57_GCOL_HEADER_SIZE;; + share->virtual_fields++; + } } else { - field_length= (uint) strpos[3]; + attr.length= (uint) strpos[3]; recpos= uint2korr(strpos+4), - pack_flag= uint2korr(strpos+6); - pack_flag&= ~FIELDFLAG_NO_DEFAULT; // Safety for old files - unireg_type= (uint) strpos[8]; + attr.pack_flag= uint2korr(strpos+6); + attr.pack_flag&= ~FIELDFLAG_NO_DEFAULT; // Safety for old files + attr.unireg_check= (Field::utype) MTYP_TYPENR((uint) strpos[8]); interval_nr= (uint) strpos[10]; /* old frm file */ - field_type= (enum_field_types) f_packtype(pack_flag); - if (f_is_binary(pack_flag)) + enum_field_types ftype= (enum_field_types) f_packtype(attr.pack_flag); + if (!(handler= Type_handler::get_handler_by_real_type(ftype))) + goto err; // Not supported field type + + if (f_is_binary(attr.pack_flag)) { /* Try to choose the best 4.1 type: @@ -1948,26 +1992,26 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, try to find a binary collation for character set. - for other types (e.g. BLOB) just use my_charset_bin. */ - if (!f_is_blob(pack_flag)) + if (!f_is_blob(attr.pack_flag)) { // 3.23 or 4.0 string - if (!(charset= get_charset_by_csname(share->table_charset->csname, - MY_CS_BINSORT, MYF(0)))) - charset= &my_charset_bin; + if (!(attr.charset= get_charset_by_csname(share->table_charset->csname, + MY_CS_BINSORT, MYF(0)))) + attr.charset= &my_charset_bin; } - else - charset= &my_charset_bin; } else - charset= share->table_charset; + attr.charset= share->table_charset; bzero((char*) &comment, sizeof(comment)); + if ((!(handler= old_frm_type_handler(attr.pack_flag, interval_nr)))) + goto err; // Not supported field type } /* Remove >32 decimals from old files */ if (share->mysql_version < 100200) - pack_flag&= ~FIELDFLAG_LONG_DECIMAL; + attr.pack_flag&= ~FIELDFLAG_LONG_DECIMAL; - if (interval_nr && charset->mbminlen > 1) + if (interval_nr && attr.charset->mbminlen > 1) { /* Unescape UCS2 intervals from HEX notation */ TYPELIB *interval= share->intervals + interval_nr - 1; @@ -1975,17 +2019,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } #ifndef TO_BE_DELETED_ON_PRODUCTION - if (field_type == MYSQL_TYPE_NEWDECIMAL && !share->mysql_version) + if (handler->real_field_type() == MYSQL_TYPE_NEWDECIMAL && + !share->mysql_version) { /* Fix pack length of old decimal values from 5.0.3 -> 5.0.4 The difference is that in the old version we stored precision in the .frm table while we now store the display_length */ - uint decimals= f_decimals(pack_flag); - field_length= my_decimal_precision_to_length(field_length, - decimals, - f_is_dec(pack_flag) == 0); + uint decimals= f_decimals(attr.pack_flag); + attr.length= + my_decimal_precision_to_length((uint) attr.length, decimals, + f_is_dec(attr.pack_flag) == 0); sql_print_error("Found incompatible DECIMAL field '%s' in %s; " "Please do \"ALTER TABLE '%s' FORCE\" to fix it!", share->fieldnames.type_names[i], share->table_name.str, @@ -2016,7 +2061,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (flags & VERS_SYSTEM_FIELD) { - switch (field_type) + switch (handler->real_field_type()) { case MYSQL_TYPE_TIMESTAMP2: case MYSQL_TYPE_DATETIME2: @@ -2038,22 +2083,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } /* Convert pre-10.2.2 timestamps to use Field::default_value */ - unireg_check= (Field::utype) MTYP_TYPENR(unireg_type); name.str= fieldnames.type_names[i]; name.length= strlen(name.str); - if (!(handler= Type_handler::get_handler_by_real_type(field_type))) - goto err; // Not supported field type + attr.interval= interval_nr ? share->intervals + interval_nr - 1 : NULL; + Record_addr addr(record + recpos, null_pos, null_bit_pos); *field_ptr= reg_field= - make_field(share, &share->mem_root, record+recpos, (uint32) field_length, - null_pos, null_bit_pos, pack_flag, handler, charset, - geom_type, srid, unireg_check, - (interval_nr ? share->intervals+interval_nr-1 : NULL), - &name, flags); + attr.make_field(share, &share->mem_root, &addr, handler, &name, flags); if (!reg_field) // Not supported field type goto err; - if (unireg_check == Field::TIMESTAMP_DNUN_FIELD || - unireg_check == Field::TIMESTAMP_DN_FIELD) + if (attr.unireg_check == Field::TIMESTAMP_DNUN_FIELD || + attr.unireg_check == Field::TIMESTAMP_DN_FIELD) { reg_field->default_value= new (&share->mem_root) Virtual_column_info(); reg_field->default_value->set_vcol_type(VCOL_DEFAULT); @@ -2077,10 +2117,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, status_var_increment(thd->status_var.feature_invisible_columns); if (!reg_field->invisible) share->visible_fields++; - if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) + if (handler->real_field_type() == MYSQL_TYPE_BIT && + !f_bit_as_char(attr.pack_flag)) { null_bits_are_used= 1; - if ((null_bit_pos+= field_length & 7) > 7) + if ((null_bit_pos+= (uint) (attr.length & 7)) > 7) { null_pos++; null_bit_pos-= 8; @@ -2103,7 +2144,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } } - if (f_no_default(pack_flag)) + if (f_no_default(attr.pack_flag)) reg_field->flags|= NO_DEFAULT_VALUE_FLAG; if (reg_field->unireg_check == Field::NEXT_NUMBER) @@ -2695,7 +2736,7 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine, if (lex->create_info.like()) return 1; // ... create select - if (lex->select_lex.item_list.elements) + if (lex->first_select_lex()->item_list.elements) return 1; // ... temporary if (create_info->tmp_table()) @@ -2884,7 +2925,7 @@ bool fix_session_vcol_expr(THD *thd, Virtual_column_info *vcol) DBUG_RETURN(0); vcol->expr->walk(&Item::cleanup_excluding_fields_processor, 0, 0); - DBUG_ASSERT(!vcol->expr->fixed); + DBUG_ASSERT(!vcol->expr->is_fixed()); DBUG_RETURN(fix_vcol_expr(thd, vcol)); } @@ -2939,7 +2980,7 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table, DBUG_PRINT("info", ("vcol: %p", vcol)); DBUG_ASSERT(func_expr); - if (func_expr->fixed) + if (func_expr->is_fixed()) DBUG_RETURN(0); // nothing to do if (fix_vcol_expr(thd, vcol)) @@ -2983,7 +3024,7 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table, of the statement because the field item does not have a field pointer at that time */ - myf warn= table->s->frm_version < FRM_VER_EXPRESSSIONS ? ME_JUST_WARNING : 0; + myf warn= table->s->frm_version < FRM_VER_EXPRESSSIONS ? ME_WARNING : 0; my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(warn), "AUTO_INCREMENT", vcol->get_vcol_type_name(), res.name); if (!warn) @@ -3440,17 +3481,6 @@ partititon_err: (my_bitmap_map*) bitmaps, share->fields, FALSE); bitmaps+= bitmap_size; - /* Don't allocate vcol_bitmap if we don't need it */ - if (share->virtual_fields) - { - if (!(outparam->def_vcol_set= (MY_BITMAP*) - alloc_root(&outparam->mem_root, sizeof(*outparam->def_vcol_set)))) - goto err; - my_bitmap_init(outparam->def_vcol_set, - (my_bitmap_map*) bitmaps, share->fields, FALSE); - bitmaps+= bitmap_size; - } - my_bitmap_init(&outparam->has_value_set, (my_bitmap_map*) bitmaps, share->fields, FALSE); bitmaps+= bitmap_size; @@ -3653,7 +3683,7 @@ void open_table_error(TABLE_SHARE *share, enum open_frm_error error, int db_errno) { char buff[FN_REFLEN]; - const myf errortype= ME_ERROR+ME_WAITTANG; // Write fatals error to log + const myf errortype= ME_ERROR_LOG; // Write fatals error to log DBUG_ENTER("open_table_error"); DBUG_PRINT("info", ("error: %d db_errno: %d", error, db_errno)); @@ -4909,7 +4939,7 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds, if (where) { - if (where->fixed) + if (where->is_fixed()) where->update_used_tables(); else if (where->fix_fields(thd, &where)) DBUG_RETURN(TRUE); @@ -4969,13 +4999,13 @@ bool TABLE_LIST::single_table_updatable() { if (!updatable) return false; - if (view && view->select_lex.table_list.elements == 1) + if (view && view->first_select_lex()->table_list.elements == 1) { /* We need to check deeply only single table views. Multi-table views will be turned to multi-table updates and then checked by leaf tables */ - return (((TABLE_LIST *)view->select_lex.table_list.first)-> + return (((TABLE_LIST *)view->first_select_lex()->table_list.first)-> single_table_updatable()); } return true; @@ -5012,7 +5042,8 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded) cond= table->on_expr->copy_andor_structure(thd); if (!table->view) DBUG_RETURN(cond); - for (TABLE_LIST *tbl= (TABLE_LIST*)table->view->select_lex.table_list.first; + for (TABLE_LIST *tbl= + (TABLE_LIST*)table->view->first_select_lex()->table_list.first; tbl; tbl= tbl->next_local) { @@ -5054,7 +5085,7 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type) { DBUG_ENTER("TABLE_LIST::prep_check_option"); bool is_cascaded= check_opt_type == VIEW_CHECK_CASCADED; - TABLE_LIST *merge_underlying_list= view->select_lex.get_table_list(); + TABLE_LIST *merge_underlying_list= view->first_select_lex()->get_table_list(); for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { /* see comment of check_opt_type parameter */ @@ -5172,7 +5203,7 @@ TABLE_LIST *TABLE_LIST::find_underlying_table(TABLE *table_to_find) if (!view) return 0; - for (TABLE_LIST *tbl= view->select_lex.get_table_list(); + for (TABLE_LIST *tbl= view->first_select_lex()->get_table_list(); tbl; tbl= tbl->next_local) { @@ -5234,7 +5265,7 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure) main_view->db.str); const char *name_table= (main_view->view ? main_view->view_name.str : main_view->table_name.str); - my_error(ER_VIEW_CHECK_FAILED, MYF(ignore_failure ? ME_JUST_WARNING : 0), + my_error(ER_VIEW_CHECK_FAILED, MYF(ignore_failure ? ME_WARNING : 0), name_db, name_table); return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR; } @@ -5278,7 +5309,7 @@ int TABLE::verify_constraints(bool ignore_failure) } field_error.append((*chk)->name.str); my_error(ER_CONSTRAINT_FAILED, - MYF(ignore_failure ? ME_JUST_WARNING : 0), field_error.c_ptr(), + MYF(ignore_failure ? ME_WARNING : 0), field_error.c_ptr(), s->db.str, s->error_table_name()); return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR; } @@ -5370,7 +5401,8 @@ bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root) { DBUG_PRINT("info", ("setting insert_value for view")); DBUG_ASSERT(is_view_or_derived() && is_merged_derived()); - for (TABLE_LIST *tbl= (TABLE_LIST*)view->select_lex.table_list.first; + for (TABLE_LIST *tbl= + (TABLE_LIST*)view->first_select_lex()->table_list.first; tbl; tbl= tbl->next_local) if (tbl->set_insert_values(mem_root)) @@ -5537,7 +5569,7 @@ void TABLE_LIST::register_want_access(ulong want_access) } if (!view) return; - for (TABLE_LIST *tbl= view->select_lex.get_table_list(); + for (TABLE_LIST *tbl= view->first_select_lex()->get_table_list(); tbl; tbl= tbl->next_local) tbl->register_want_access(want_access); @@ -5729,6 +5761,7 @@ void TABLE_LIST::set_check_materialized() The subtree should be already excluded */ DBUG_ASSERT(!derived->first_select()->first_inner_unit() || + derived->first_select()->first_inner_unit()->with_element || derived->first_select()->first_inner_unit()->first_select()-> exclude_from_table_unique_test); } @@ -5745,14 +5778,14 @@ TABLE *TABLE_LIST::get_real_join_table() break; /* we do not support merging of union yet */ DBUG_ASSERT(tbl->view == NULL || - tbl->view->select_lex.next_select() == NULL); + tbl->view->first_select_lex()->next_select() == NULL); DBUG_ASSERT(tbl->derived == NULL || tbl->derived->first_select()->next_select() == NULL); { List_iterator_fast<TABLE_LIST> ti(tbl->view != NULL ? - tbl->view->select_lex.top_join_list : + tbl->view->first_select_lex()->top_join_list : tbl->derived->first_select()->top_join_list); for (;;) { @@ -5923,7 +5956,7 @@ Item *Field_iterator_view::create_item(THD *thd) Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, LEX_CSTRING *name) { - bool save_wrapper= thd->lex->select_lex.no_wrap_view_item; + bool save_wrapper= thd->lex->first_select_lex()->no_wrap_view_item; Item *field= *field_ref; DBUG_ENTER("create_view_field"); @@ -5934,13 +5967,13 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, ('mysql_schema_table' function). So we can return directly the field. This case happens only for 'show & where' commands. */ - DBUG_ASSERT(field && field->fixed); + DBUG_ASSERT(field && field->is_fixed()); DBUG_RETURN(field); } DBUG_ASSERT(field); thd->lex->current_select->no_wrap_view_item= TRUE; - if (!field->fixed) + if (!field->is_fixed()) { if (field->fix_fields(thd, field_ref)) { @@ -5954,8 +5987,9 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, { DBUG_RETURN(field); } - Name_resolution_context *context= view->view ? &view->view->select_lex.context : - &thd->lex->select_lex.context; + Name_resolution_context *context= (view->view ? + &view->view->first_select_lex()->context: + &thd->lex->first_select_lex()->context); Item *item= (new (thd->mem_root) Item_direct_view_ref(thd, context, field_ref, view->alias.str, name, view)); @@ -6278,13 +6312,12 @@ void TABLE::clear_column_bitmaps() Reset column read/write usage. It's identical to: bitmap_clear_all(&table->def_read_set); bitmap_clear_all(&table->def_write_set); - if (s->virtual_fields) bitmap_clear_all(table->def_vcol_set); The code assumes that the bitmaps are allocated after each other, as guaranteed by open_table_from_share() */ bzero((char*) def_read_set.bitmap, s->column_bitmap_size * (s->virtual_fields ? 3 : 2)); - column_bitmaps_set(&def_read_set, &def_write_set, def_vcol_set); + column_bitmaps_set(&def_read_set, &def_write_set); rpl_write_set= 0; // Safety } @@ -6431,13 +6464,8 @@ void TABLE::mark_columns_needed_for_delete() Field **reg_field; for (reg_field= field ; *reg_field ; reg_field++) { - Field *cur_field= *reg_field; - if (cur_field->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG)) - { - bitmap_set_bit(read_set, cur_field->field_index); - if (cur_field->vcol_info) - bitmap_set_bit(vcol_set, cur_field->field_index); - } + if ((*reg_field)->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG)) + mark_column_with_deps(*reg_field); } need_signal= true; } @@ -6516,13 +6544,7 @@ void TABLE::mark_columns_needed_for_update() if (any_written && !all_read) { for (KEY_PART_INFO *kp= k->key_part; kp < kpend; kp++) - { - int idx= kp->fieldnr - 1; - if (bitmap_fast_test_and_set(read_set, idx)) - continue; - if (field[idx]->vcol_info) - mark_virtual_col(field[idx]); - } + mark_column_with_deps(field[kp->fieldnr - 1]); } } need_signal= true; @@ -6719,49 +6741,12 @@ void TABLE::mark_columns_per_binlog_row_image() DBUG_ASSERT(FALSE); } } - /* - We have to ensure that all virtual columns that are part of read set - are calculated. - */ - if (vcol_set) - bitmap_union(vcol_set, read_set); file->column_bitmaps_signal(); } DBUG_VOID_RETURN; } -/* - @brief Mark a column as virtual used by the query - - @param field the field for the column to be marked - - @details - The function marks the column for 'field' as virtual (computed) - in the bitmap vcol_set. - If the column is marked for the first time the expression to compute - the column is traversed and all columns that are occurred there are - marked in the read_set of the table. - - @retval - TRUE if column is marked for the first time - @retval - FALSE otherwise -*/ - -bool TABLE::mark_virtual_col(Field *field) -{ - bool res; - DBUG_ASSERT(field->vcol_info); - if (!(res= bitmap_fast_test_and_set(vcol_set, field->field_index))) - { - Item *vcol_item= field->vcol_info->expr; - DBUG_ASSERT(vcol_item); - vcol_item->walk(&Item::register_field_in_read_map, 1, 0); - } - return res; -} - /* @brief Mark virtual columns for update/insert commands @@ -6803,13 +6788,13 @@ bool TABLE::mark_virtual_columns_for_write(bool insert_fl { tmp_vfield= *vfield_ptr; if (bitmap_is_set(write_set, tmp_vfield->field_index)) - bitmap_updated|= mark_virtual_col(tmp_vfield); + bitmap_updated|= mark_virtual_column_with_deps(tmp_vfield); else if (tmp_vfield->vcol_info->stored_in_db || (tmp_vfield->flags & (PART_KEY_FLAG | FIELD_IN_PART_FUNC_FLAG | PART_INDIRECT_KEY_FLAG))) { bitmap_set_bit(write_set, tmp_vfield->field_index); - mark_virtual_col(tmp_vfield); + mark_virtual_column_with_deps(tmp_vfield); bitmap_updated= true; } } @@ -6940,8 +6925,6 @@ void TABLE::mark_columns_used_by_virtual_fields(void) void TABLE::mark_check_constraint_columns_for_read(void) { bitmap_union(read_set, s->check_set); - if (vcol_set) - bitmap_union(vcol_set, s->check_set); } @@ -7695,6 +7678,20 @@ public: } }; + +/* + to satisfy ASSERT_COLUMN_MARKED_FOR_WRITE Field's assert we temporarily + mark field for write before storing the generated value in it +*/ +#ifndef DBUG_OFF +#define DBUG_FIX_WRITE_SET(f) bool _write_set_fixed= !bitmap_fast_test_and_set(write_set, (f)->field_index) +#define DBUG_RESTORE_WRITE_SET(f) if (_write_set_fixed) bitmap_clear_bit(write_set, (f)->field_index) +#else +#define DBUG_FIX_WRITE_SET(f) +#define DBUG_RESTORE_WRITE_SET(f) +#endif + + /* @brief Compute values for virtual columns used in query @@ -7758,17 +7755,17 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) switch (update_mode) { case VCOL_UPDATE_FOR_READ: update= (!vcol_info->stored_in_db && - bitmap_is_set(vcol_set, vf->field_index)); + bitmap_is_set(read_set, vf->field_index)); swap_values= 1; break; case VCOL_UPDATE_FOR_DELETE: case VCOL_UPDATE_FOR_WRITE: - update= bitmap_is_set(vcol_set, vf->field_index); + update= bitmap_is_set(read_set, vf->field_index); break; case VCOL_UPDATE_FOR_REPLACE: update= ((!vcol_info->stored_in_db && (vf->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG)) && - bitmap_is_set(vcol_set, vf->field_index)) || + bitmap_is_set(read_set, vf->field_index)) || update_all_columns); if (update && (vf->flags & BLOB_FLAG)) { @@ -7788,7 +7785,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) /* Read indexed fields that was not updated in VCOL_UPDATE_FOR_READ */ update= (!vcol_info->stored_in_db && (vf->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG)) && - !bitmap_is_set(vcol_set, vf->field_index)); + !bitmap_is_set(read_set, vf->field_index)); swap_values= 1; break; } @@ -7797,8 +7794,10 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) { int field_error __attribute__((unused)) = 0; /* Compute the actual value of the virtual fields */ + DBUG_FIX_WRITE_SET(vf); if (vcol_info->expr->save_in_field(vf, 0)) field_error= error= 1; + DBUG_RESTORE_WRITE_SET(vf); DBUG_PRINT("info", ("field '%s' - updated error: %d", vf->field_name.str, field_error)); if (swap_values && (vf->flags & BLOB_FLAG)) @@ -7832,7 +7831,9 @@ int TABLE::update_virtual_field(Field *vf) in_use->set_n_backup_active_arena(expr_arena, &backup_arena); bitmap_clear_all(&tmp_set); vf->vcol_info->expr->walk(&Item::update_vcol_processor, 0, &tmp_set); + DBUG_FIX_WRITE_SET(vf); vf->vcol_info->expr->save_in_field(vf, 0); + DBUG_RESTORE_WRITE_SET(vf); in_use->restore_active_arena(expr_arena, &backup_arena); DBUG_RETURN(in_use->is_error()); } @@ -8497,200 +8498,6 @@ double KEY::actual_rec_per_key(uint i) } -/** - @brief - Mark subformulas of a condition unusable for the condition pushed into table - - @param cond The condition whose subformulas are to be marked - - @details - This method recursively traverses the AND-OR condition cond and for each subformula - of the codition it checks whether it can be usable for the extraction of a condition - that can be pushed into this table. The subformulas that are not usable are - marked with the flag NO_EXTRACTION_FL. - @note - This method is called before any call of TABLE_LIST::build_pushable_cond_for_table. - The flag NO_EXTRACTION_FL set in a subformula allows to avoid building clone - for the subformula when extracting the pushable condition. -*/ - -void TABLE_LIST::check_pushable_cond_for_table(Item *cond) -{ - table_map tab_map= table->map; - cond->clear_extraction_flag(); - if (cond->type() == Item::COND_ITEM) - { - bool and_cond= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); - uint count= 0; - Item *item; - while ((item=li++)) - { - check_pushable_cond_for_table(item); - if (item->get_extraction_flag() != NO_EXTRACTION_FL) - count++; - else if (!and_cond) - break; - } - if ((and_cond && count == 0) || item) - { - cond->set_extraction_flag(NO_EXTRACTION_FL); - if (and_cond) - li.rewind(); - while ((item= li++)) - item->clear_extraction_flag(); - } - } - else if (!cond->excl_dep_on_table(tab_map)) - cond->set_extraction_flag(NO_EXTRACTION_FL); -} - - -/** - @brief - Build condition extractable from the given one depended only on this table - - @param thd The thread handle - @param cond The condition from which the pushable one is to be extracted - - @details - For the given condition cond this method finds out what condition depended - only on this table can be extracted from cond. If such condition C exists - the method builds the item for it. - The method uses the flag NO_EXTRACTION_FL set by the preliminary call of - the method TABLE_LIST::check_pushable_cond_for_table to figure out whether - a subformula depends only on this table or not. - @note - The built condition C is always implied by the condition cond - (cond => C). The method tries to build the most restictive such - condition (i.e. for any other condition C' such that cond => C' - we have C => C'). - @note - The build item is not ready for usage: substitution for the field items - has to be done and it has to be re-fixed. - - @retval - the built condition pushable into this table if such a condition exists - NULL if there is no such a condition -*/ - -Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond) -{ - table_map tab_map= table->map; - bool is_multiple_equality= cond->type() == Item::FUNC_ITEM && - ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC; - if (cond->get_extraction_flag() == NO_EXTRACTION_FL) - return 0; - if (cond->type() == Item::COND_ITEM) - { - bool cond_and= false; - Item_cond *new_cond; - if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) - { - cond_and= true; - new_cond=new (thd->mem_root) Item_cond_and(thd); - } - else - new_cond= new (thd->mem_root) Item_cond_or(thd); - if (!new_cond) - return 0; - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); - Item *item; - bool is_fix_needed= false; - while ((item=li++)) - { - if (item->get_extraction_flag() == NO_EXTRACTION_FL) - { - if (!cond_and) - return 0; - continue; - } - Item *fix= build_pushable_cond_for_table(thd, item); - if (!fix && !cond_and) - return 0; - if (!fix) - continue; - - if (fix->type() == Item::COND_ITEM && - ((Item_cond*) fix)->functype() == Item_func::COND_AND_FUNC) - is_fix_needed= true; - - new_cond->argument_list()->push_back(fix, thd->mem_root); - } - if (is_fix_needed && new_cond->fix_fields(thd, 0)) - return 0; - - switch (new_cond->argument_list()->elements) - { - case 0: - return 0; - case 1: - return new_cond->argument_list()->head(); - default: - return new_cond; - } - } - else if (is_multiple_equality) - { - if (!(cond->used_tables() & tab_map)) - return 0; - Item *new_cond= NULL; - int i= 0; - Item_equal *item_equal= (Item_equal *) cond; - Item *left_item = item_equal->get_const(); - Item_equal_fields_iterator it(*item_equal); - Item *item; - if (!left_item) - { - while ((item=it++)) - if (item->used_tables() == tab_map) - { - left_item= item; - break; - } - } - if (!left_item) - return 0; - while ((item=it++)) - { - if (!(item->used_tables() == tab_map)) - continue; - Item_func_eq *eq= 0; - Item *left_item_clone= left_item->build_clone(thd); - Item *right_item_clone= item->build_clone(thd); - if (left_item_clone && right_item_clone) - { - left_item_clone->set_item_equal(NULL); - right_item_clone->set_item_equal(NULL); - eq= new (thd->mem_root) Item_func_eq(thd, right_item_clone, - left_item_clone); - } - if (eq) - { - i++; - switch (i) - { - case 1: - new_cond= eq; - break; - case 2: - new_cond= new (thd->mem_root) Item_cond_and(thd, new_cond, eq); - break; - default: - ((Item_cond_and*)new_cond)->argument_list()->push_back(eq, - thd->mem_root); - } - } - } - if (new_cond) - new_cond->fix_fields(thd, &new_cond); - return new_cond; - } - else if (cond->get_extraction_flag() != NO_EXTRACTION_FL) - return cond->build_clone(thd); - return 0; -} - LEX_CSTRING *fk_option_name(enum_fk_option opt) { static LEX_CSTRING names[]= @@ -8778,7 +8585,7 @@ bool TR_table::update(ulonglong start_id, ulonglong end_id) store(FLD_BEGIN_TS, thd->transaction_time()); thd->set_time(); - timeval end_time= {thd->query_start(), long(thd->query_start_sec_part())}; + timeval end_time= {thd->query_start(), int(thd->query_start_sec_part())}; store(FLD_TRX_ID, start_id); store(FLD_COMMIT_ID, end_id); store(FLD_COMMIT_TS, end_time); @@ -8799,7 +8606,7 @@ bool TR_table::query(ulonglong trx_id) READ_RECORD info; int error; List<TABLE_LIST> dummy; - SELECT_LEX &slex= thd->lex->select_lex; + SELECT_LEX &slex= *(thd->lex->first_select_lex()); Name_resolution_context_backup backup(slex.context, *this); Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_TRX_ID]); Item *value= newx Item_int(thd, trx_id); @@ -8829,7 +8636,7 @@ bool TR_table::query(MYSQL_TIME &commit_time, bool backwards) READ_RECORD info; int error; List<TABLE_LIST> dummy; - SELECT_LEX &slex= thd->lex->select_lex; + SELECT_LEX &slex= *(thd->lex->first_select_lex()); Name_resolution_context_backup backup(slex.context, *this); Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_COMMIT_TS]); Item *value= newx Item_datetime_literal(thd, &commit_time, 6); @@ -8859,7 +8666,7 @@ bool TR_table::query(MYSQL_TIME &commit_time, bool backwards) if (res > 0) { MYSQL_TIME commit_ts; - if ((*this)[FLD_COMMIT_TS]->get_date(&commit_ts, 0)) + if ((*this)[FLD_COMMIT_TS]->get_date(&commit_ts, date_mode_t(0))) { found= false; break; @@ -9078,7 +8885,8 @@ bool Vers_history_point::resolve_unit(THD *thd) return false; if (item->fix_fields_if_needed(thd, &item)) return true; - return item->this_item()->type_handler_for_system_time()-> + return item->this_item()->real_type_handler()-> + type_handler_for_system_time()-> Vers_history_point_resolve_unit(thd, this); } diff --git a/sql/table.h b/sql/table.h index 7236fb91b0f..2b3fe1c20d4 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1155,8 +1155,6 @@ public: MY_BITMAP cond_set; /* used to mark fields from sargable conditions*/ /* Active column sets */ MY_BITMAP *read_set, *write_set, *rpl_write_set; - /* Set if using virtual fields */ - MY_BITMAP *vcol_set, *def_vcol_set; /* On INSERT: fields that the user specified a value for */ MY_BITMAP has_value_set; @@ -1377,7 +1375,9 @@ public: void mark_columns_needed_for_delete(void); void mark_columns_needed_for_insert(void); void mark_columns_per_binlog_row_image(void); - bool mark_virtual_col(Field *field); + inline bool mark_column_with_deps(Field *field); + inline bool mark_virtual_column_with_deps(Field *field); + inline void mark_virtual_column_deps(Field *field); bool mark_virtual_columns_for_write(bool insert_fl); bool check_virtual_columns_marked_for_read(); bool check_virtual_columns_marked_for_write(); @@ -1399,39 +1399,21 @@ public: if (file) file->column_bitmaps_signal(); } - inline void column_bitmaps_set(MY_BITMAP *read_set_arg, - MY_BITMAP *write_set_arg, - MY_BITMAP *vcol_set_arg) - { - read_set= read_set_arg; - write_set= write_set_arg; - vcol_set= vcol_set_arg; - if (file) - file->column_bitmaps_signal(); - } inline void column_bitmaps_set_no_signal(MY_BITMAP *read_set_arg, MY_BITMAP *write_set_arg) { read_set= read_set_arg; write_set= write_set_arg; } - inline void column_bitmaps_set_no_signal(MY_BITMAP *read_set_arg, - MY_BITMAP *write_set_arg, - MY_BITMAP *vcol_set_arg) - { - read_set= read_set_arg; - write_set= write_set_arg; - vcol_set= vcol_set_arg; - } inline void use_all_columns() { column_bitmaps_set(&s->all_set, &s->all_set); } + inline void use_all_stored_columns(); inline void default_column_bitmaps() { read_set= &def_read_set; write_set= &def_write_set; - vcol_set= def_vcol_set; /* Note that this may be 0 */ rpl_write_set= 0; } /** Should this instance of the table be reopened? */ @@ -2611,8 +2593,6 @@ struct TABLE_LIST return false; } void set_lock_type(THD* thd, enum thr_lock_type lock); - void check_pushable_cond_for_table(Item *cond); - Item *build_pushable_cond_for_table(THD *thd, Item *cond); void remove_join_columns() { diff --git a/sql/table_cache.cc b/sql/table_cache.cc index cb9583a2440..682f9455d26 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -645,7 +645,7 @@ bool tdc_init(void) void tdc_start_shutdown(void) { - DBUG_ENTER("table_def_start_shutdown"); + DBUG_ENTER("tdc_start_shutdown"); if (tdc_inited) { /* diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index 0cc683c631d..67a8e783208 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -70,12 +70,16 @@ static DWORD fls; static bool skip_completion_port_on_success = false; +PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ() +{ + return pool? &callback_environ: 0; +} + /* Threadpool callbacks. io_completion_callback - handle client request timer_callback - handle wait timeout (kill connection) - shm_read_callback, shm_close_callback - shared memory stuff login_callback - user login (submitted as threadpool work) */ @@ -89,9 +93,6 @@ static void CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance, static void CALLBACK work_callback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK work); -static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance, - PVOID Context, PTP_WAIT wait,TP_WAIT_RESULT wait_result); - static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance); /* Get current time as Windows time */ @@ -120,7 +121,6 @@ public: PTP_CALLBACK_INSTANCE callback_instance; PTP_IO io; PTP_TIMER timer; - PTP_WAIT shm_read; PTP_WORK work; bool long_callback; @@ -139,7 +139,15 @@ struct TP_connection *new_TP_connection(CONNECT *connect) void TP_pool_win::add(TP_connection *c) { - SubmitThreadpoolWork(((TP_connection_win *)c)->work); + if(FlsGetValue(fls)) + { + /* Inside threadpool(), execute callback directly. */ + tp_callback(c); + } + else + { + SubmitThreadpoolWork(((TP_connection_win *)c)->work); + } } @@ -149,7 +157,6 @@ TP_connection_win::TP_connection_win(CONNECT *c) : callback_instance(0), io(0), timer(0), - shm_read(0), work(0) { } @@ -170,30 +177,20 @@ int TP_connection_win::init() case VIO_TYPE_NAMEDPIPE: handle= (HANDLE)vio->hPipe; break; - case VIO_TYPE_SHARED_MEMORY: - handle= vio->event_server_wrote; - break; default: abort(); } - if (vio_type == VIO_TYPE_SHARED_MEMORY) - { - CHECK_ALLOC_ERROR(shm_read= CreateThreadpoolWait(shm_read_callback, this, &callback_environ)); - } - else + + /* Performance tweaks (s. MSDN documentation)*/ + UCHAR flags= FILE_SKIP_SET_EVENT_ON_HANDLE; + if (skip_completion_port_on_success) { - /* Performance tweaks (s. MSDN documentation)*/ - UCHAR flags= FILE_SKIP_SET_EVENT_ON_HANDLE; - if (skip_completion_port_on_success) - { - flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; - } - (void)SetFileCompletionNotificationModes(handle, flags); - /* Assign io completion callback */ - CHECK_ALLOC_ERROR(io= CreateThreadpoolIo(handle, io_completion_callback, this, &callback_environ)); + flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; } - + (void)SetFileCompletionNotificationModes(handle, flags); + /* Assign io completion callback */ + CHECK_ALLOC_ERROR(io= CreateThreadpoolIo(handle, io_completion_callback, this, &callback_environ)); CHECK_ALLOC_ERROR(timer= CreateThreadpoolTimer(timer_callback, this, &callback_environ)); CHECK_ALLOC_ERROR(work= CreateThreadpoolWork(work_callback, this, &callback_environ)); return 0; @@ -214,11 +211,6 @@ int TP_connection_win::start_io() DWORD last_error= 0; int retval; - if (shm_read) - { - SetThreadpoolWait(shm_read, handle, NULL); - return 0; - } StartThreadpoolIo(io); if (vio_type == VIO_TYPE_TCPIP || vio_type == VIO_TYPE_SSL) @@ -297,9 +289,6 @@ TP_connection_win::~TP_connection_win() if (io) CloseThreadpoolIo(io); - if (shm_read) - CloseThreadpoolWait(shm_read); - if (work) CloseThreadpoolWork(work); @@ -312,14 +301,13 @@ TP_connection_win::~TP_connection_win() void TP_connection_win::wait_begin(int type) { - /* Signal to the threadpool whenever callback can run long. Currently, binlog waits are a good candidate, its waits are really long */ if (type == THD_WAIT_BINLOG) { - if (!long_callback) + if (!long_callback && callback_instance) { CallbackMayRunLong(callback_instance); long_callback= true; @@ -332,12 +320,11 @@ void TP_connection_win::wait_end() /* Do we need to do anything ? */ } -/* - This function should be called first whenever a callback is invoked in the +/* + This function should be called first whenever a callback is invoked in the threadpool, does my_thread_init() if not yet done */ -extern ulong thread_created; -static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance) +void tp_win_callback_prolog() { if (FlsGetValue(fls) == NULL) { @@ -347,6 +334,12 @@ static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance) InterlockedIncrement((volatile long *)&tp_stats.num_worker_threads); my_thread_init(); } +} + +extern ulong thread_created; +static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance) +{ + tp_win_callback_prolog(); TP_connection_win *c = (TP_connection_win *)context; c->callback_instance = instance; c->long_callback = false; @@ -420,29 +413,6 @@ static VOID CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance, } } - -/* - Shared memory read callback. - Invoked when read event is set on connection. -*/ - -static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance, - PVOID context, PTP_WAIT wait,TP_WAIT_RESULT wait_result) -{ - TP_connection_win *c= (TP_connection_win *)context; - /* Disarm wait. */ - SetThreadpoolWait(wait, NULL, NULL); - - /* - This is an autoreset event, and one wakeup is eaten already by threadpool, - and the current state is "not set". Thus we need to reset the event again, - or vio_read will hang. - */ - SetEvent(c->handle); - tp_callback(instance, context); -} - - static void CALLBACK work_callback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK work) { tp_callback(instance, context); diff --git a/sql/unireg.cc b/sql/unireg.cc index 6540e11578b..4692b2d74d1 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -897,32 +897,12 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, while ((field=it++)) { uint recpos; - int2store(buff+3, field->length); /* The +1 is here becasue the col offset in .frm file have offset 1 */ recpos= field->offset+1 + (uint) data_offset; int3store(buff+5,recpos); - int2store(buff+8,field->pack_flag); - buff[10]= (uchar) field->unireg_check; buff[12]= (uchar) field->interval_id; - buff[13]= (uchar) field->real_field_type(); - if (field->real_field_type() == MYSQL_TYPE_GEOMETRY) - { - buff[11]= 0; - buff[14]= (uchar) field->geom_type; -#ifndef HAVE_SPATIAL - DBUG_ASSERT(0); // Should newer happen -#endif - } - else if (field->charset) - { - buff[11]= (uchar) (field->charset->number >> 8); - buff[14]= (uchar) field->charset->number; - } - else - { - buff[11]= buff[14]= 0; // Numerical - } - + buff[13]= (uchar) field->type_handler()->real_field_type(); + field->type_handler()->Column_definition_attributes_frm_pack(field, buff); int2store(buff+15, field->comment.length); comment_length+= field->comment.length; set_if_bigger(int_count,field->interval_id); @@ -1043,21 +1023,16 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values while ((field=it++)) { + Record_addr addr(buff + field->offset + data_offset, + null_pos + null_count / 8, null_count & 7); + Column_definition_attributes tmp(*field); + tmp.interval= field->save_interval ? + field->save_interval : field->interval; /* regfield don't have to be deleted as it's allocated on THD::mem_root */ - Field *regfield= make_field(&share, thd->mem_root, - buff+field->offset + data_offset, - (uint32)field->length, - null_pos + null_count / 8, - null_count & 7, - field->pack_flag, - field->type_handler(), - field->charset, - field->geom_type, field->srid, - field->unireg_check, - field->save_interval ? field->save_interval - : field->interval, - &field->field_name, - field->flags); + Field *regfield= tmp.make_field(&share, thd->mem_root, &addr, + field->type_handler(), + &field->field_name, + field->flags); if (!regfield) { error= 1; diff --git a/sql/unireg.h b/sql/unireg.h index 88d0c882824..6f224ab4894 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -56,11 +56,6 @@ #endif #define ER_THD_OR_DEFAULT(thd,X) ((thd) ? ER_THD(thd, (X)) : ER_DEFAULT(X)) - -#define ME_INFO (ME_HOLDTANG | ME_NOREFRESH) -#define ME_ERROR (ME_BELL | ME_NOREFRESH) -#define MYF_RW MYF(MY_WME+MY_NABP) /* Vid my_read & my_write */ - #define SPECIAL_USE_LOCKS 1 /* Lock used databases */ #define SPECIAL_NO_NEW_FUNC 2 /* Skip new functions */ #define SPECIAL_SKIP_SHOW_DB 4 /* Don't allow 'show db' */ diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 89eac6b3d00..bc52bff0de9 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1363,7 +1363,7 @@ static int create_view_query(THD *thd, uchar** buf, size_t* buf_len) { LEX *lex= thd->lex; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); TABLE_LIST *first_table= select_lex->table_list.first; TABLE_LIST *views = first_table; LEX_USER *definer; @@ -1445,7 +1445,7 @@ static int wsrep_drop_table_query(THD* thd, uchar** buf, size_t* buf_len) { LEX* lex= thd->lex; - SELECT_LEX* select_lex= &lex->select_lex; + SELECT_LEX* select_lex= lex->first_select_lex(); TABLE_LIST* first_table= select_lex->table_list.first; String buff; @@ -1516,7 +1516,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, DBUG_ASSERT(table_list || db); LEX* lex= thd->lex; - SELECT_LEX* select_lex= &lex->select_lex; + SELECT_LEX* select_lex= lex->first_select_lex(); TABLE_LIST* first_table= select_lex->table_list.first; switch (lex->sql_command) @@ -2730,10 +2730,11 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table, } return(false); - -WSREP_ERROR_LABEL: +#ifdef WITH_WSREP +wsrep_error_label: thd->wsrep_TOI_pre_query= NULL; return (true); +#endif } |