diff options
-rw-r--r-- | mysql-test/main/func_analyse.result | 45 | ||||
-rw-r--r-- | mysql-test/main/func_analyse.test | 44 | ||||
-rw-r--r-- | mysql-test/main/func_misc.result | 26 | ||||
-rw-r--r-- | mysql-test/main/func_misc.test | 31 | ||||
-rw-r--r-- | sql/field.cc | 30 | ||||
-rw-r--r-- | sql/field.h | 25 | ||||
-rw-r--r-- | sql/item.h | 155 | ||||
-rw-r--r-- | sql/item_func.h | 12 | ||||
-rw-r--r-- | sql/item_row.h | 5 | ||||
-rw-r--r-- | sql/item_sum.cc | 8 | ||||
-rw-r--r-- | sql/item_sum.h | 12 | ||||
-rw-r--r-- | sql/item_xmlfunc.cc | 13 | ||||
-rw-r--r-- | sql/procedure.h | 10 | ||||
-rw-r--r-- | sql/sql_insert.cc | 6 | ||||
-rw-r--r-- | sql/sql_select.cc | 489 | ||||
-rw-r--r-- | sql/sql_select.h | 10 |
16 files changed, 611 insertions, 310 deletions
diff --git a/mysql-test/main/func_analyse.result b/mysql-test/main/func_analyse.result index 1e78e603bca..068693bd028 100644 --- a/mysql-test/main/func_analyse.result +++ b/mysql-test/main/func_analyse.result @@ -171,3 +171,48 @@ SELECT (SELECT 1 FROM t1 PROCEDURE ANALYSE()) FROM t2; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'PROCEDURE ANALYSE()) FROM t2' at line 1 SELECT ((SELECT 1 FROM t1 PROCEDURE ANALYSE())) FROM t2; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'PROCEDURE ANALYSE())) FROM t2' at line 1 +# +# Start of 10.4 tests +# +# +# MDEV-16309 Split ::create_tmp_field() into virtual methods in Item +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +BEGIN NOT ATOMIC +DECLARE rec ROW(Field_name TEXT, +Min_value TEXT, +Max_value TEXT, +Min_length TEXT, +Max_length TEXT, +Empties_or_zeros TEXT, +Nulls TEXT, +Avg_value_or_avg_length TEXT, +Std TEXT, +Optimal_fieldtype TEXT); +DECLARE c CURSOR FOR SELECT * FROM t1 PROCEDURE analyse(); +OPEN c; +FETCH c INTO rec; +CLOSE c; +SELECT rec.field_name, +rec.Min_value, rec.Max_value, +rec.Min_length, rec. Max_length, +rec.Empties_or_zeros, rec.Nulls, +rec.Avg_value_or_avg_length, rec.Std, +rec.Optimal_fieldtype; +END; +$$ +rec.field_name test.t1.a +rec.Min_value 1 +rec.Max_value 3 +rec.Min_length 1 +rec. Max_length 1 +rec.Empties_or_zeros 0 +rec.Nulls 0 +rec.Avg_value_or_avg_length 2.0000 +rec.Std 0.8165 +rec.Optimal_fieldtype ENUM('1','2','3') NOT NULL +DROP TABLE t1; +# +# End of 10.4 tests +# diff --git a/mysql-test/main/func_analyse.test b/mysql-test/main/func_analyse.test index d99f5c0fa9a..ae3d6d910e5 100644 --- a/mysql-test/main/func_analyse.test +++ b/mysql-test/main/func_analyse.test @@ -181,3 +181,47 @@ SELECT * FROM t1 NATURAL JOIN (SELECT * FROM t2 PROCEDURE ANALYSE()); SELECT (SELECT 1 FROM t1 PROCEDURE ANALYSE()) FROM t2; --error ER_PARSE_ERROR SELECT ((SELECT 1 FROM t1 PROCEDURE ANALYSE())) FROM t2; + + +--echo # +--echo # Start of 10.4 tests +--echo # + +--echo # +--echo # MDEV-16309 Split ::create_tmp_field() into virtual methods in Item +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +--vertical_results +DELIMITER $$; +BEGIN NOT ATOMIC + DECLARE rec ROW(Field_name TEXT, + Min_value TEXT, + Max_value TEXT, + Min_length TEXT, + Max_length TEXT, + Empties_or_zeros TEXT, + Nulls TEXT, + Avg_value_or_avg_length TEXT, + Std TEXT, + Optimal_fieldtype TEXT); + DECLARE c CURSOR FOR SELECT * FROM t1 PROCEDURE analyse(); + OPEN c; + FETCH c INTO rec; + CLOSE c; + SELECT rec.field_name, + rec.Min_value, rec.Max_value, + rec.Min_length, rec. Max_length, + rec.Empties_or_zeros, rec.Nulls, + rec.Avg_value_or_avg_length, rec.Std, + rec.Optimal_fieldtype; +END; +$$ +DELIMITER ;$$ +--horizontal_results +DROP TABLE t1; + +--echo # +--echo # End of 10.4 tests +--echo # diff --git a/mysql-test/main/func_misc.result b/mysql-test/main/func_misc.result index e7a416f8ea6..e7570004700 100644 --- a/mysql-test/main/func_misc.result +++ b/mysql-test/main/func_misc.result @@ -1562,3 +1562,29 @@ Catalog Database Table Table_alias Column Column_alias Type Length Max length Is def INET_ATON("255.255.255.255.255.255.255.255") 8 21 20 Y 32928 0 63 INET_ATON("255.255.255.255.255.255.255.255") 18446744073709551615 +# +# End of 10.3 tests +# +# +# Start of 10.4 tests +# +# +# MDEV-16309 Split ::create_tmp_field() into virtual methods in Item +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +BEGIN NOT ATOMIC +DECLARE a TEXT; +DECLARE c CURSOR FOR SELECT NAME_CONST('x','y') FROM t1; +OPEN c; +FETCH c INTO a; +CLOSE c; +SELECT a; +END; +$$ +a +y +DROP TABLE t1; +# +# End of 10.4 tests +# diff --git a/mysql-test/main/func_misc.test b/mysql-test/main/func_misc.test index f144236bfdb..9b9c9476420 100644 --- a/mysql-test/main/func_misc.test +++ b/mysql-test/main/func_misc.test @@ -1203,3 +1203,34 @@ SELECT INET_ATON("255.255.255.255.255.255.255.255"); --enable_ps_protocol --disable_metadata + +--echo # +--echo # End of 10.3 tests +--echo # + +--echo # +--echo # Start of 10.4 tests +--echo # + +--echo # +--echo # MDEV-16309 Split ::create_tmp_field() into virtual methods in Item +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +DELIMITER $$; +BEGIN NOT ATOMIC + DECLARE a TEXT; + DECLARE c CURSOR FOR SELECT NAME_CONST('x','y') FROM t1; + OPEN c; + FETCH c INTO a; + CLOSE c; + SELECT a; +END; +$$ +DELIMITER ;$$ +DROP TABLE t1; + +--echo # +--echo # End of 10.4 tests +--echo # diff --git a/sql/field.cc b/sql/field.cc index faa80caf70b..220785643bd 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2331,6 +2331,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) diff --git a/sql/field.h b/sql/field.h index ab8d66a7b8d..5313f35d81a 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1228,6 +1228,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); @@ -1388,7 +1394,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; + } /* maximum possible display length */ virtual uint32 max_display_length()= 0; @@ -2307,6 +2325,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); diff --git a/sql/item.h b/sql/item.h index 64e60e7fec2..cd6a993b869 100644 --- a/sql/item.h +++ b/sql/item.h @@ -99,7 +99,9 @@ class sp_head; class Protocol; struct TABLE_LIST; void item_init(void); /* Init item functions */ +class Item_result_field; class Item_field; +class Item_ref; class Item_param; class user_var_entry; class JOIN; @@ -603,6 +605,70 @@ public: }; +/** + 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: public Value_source, public Type_all_attributes { @@ -635,7 +701,7 @@ public: 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, + EXPR_CACHE_ITEM, DATE_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; @@ -682,6 +748,24 @@ protected: 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); void push_note_converted_to_negative_complement(THD *thd); @@ -1540,7 +1624,6 @@ 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; } @@ -1817,11 +1900,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 bool vers_trx_id() const @@ -2374,6 +2463,8 @@ protected: value == ((Item_basic_value*)item)->val_int() && (value >= 0 || item->unsigned_flag == unsigned_flag); } + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param); }; @@ -2455,6 +2546,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) @@ -2733,6 +2829,16 @@ 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 create_tmp_field_ex_simple(table, src, param); + } int save_in_field(Field *field, bool no_conversions) { return value_item->save_in_field(field, no_conversions); @@ -2778,13 +2884,15 @@ public: {} ~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) { @@ -2877,6 +2985,12 @@ public: Type_std_attributes::set(par_field); } 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); } @@ -2992,6 +3106,11 @@ public: 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(); } uint32 field_flags() const { @@ -3048,7 +3167,6 @@ public: cond_equal_ref); } bool is_result_field() { return false; } - void set_result_field(Field *field_arg) {} void save_in_result_field(bool no_conversions) { } Item *get_tmp_table_item(THD *thd); bool collect_item_field_processor(void * arg); @@ -3257,6 +3375,12 @@ public: return result_field->type_handler(); } #endif + 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); @@ -3675,8 +3799,6 @@ public: 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; } @@ -4800,6 +4922,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, @@ -5549,6 +5673,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; } @@ -6518,7 +6648,8 @@ public: 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) + 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), diff --git a/sql/item_func.h b/sql/item_func.h index 5c2a67645b4..59fc49ead39 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -2358,8 +2358,8 @@ 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); @@ -2534,6 +2534,12 @@ 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;} double val_real(); @@ -2839,6 +2845,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 ? diff --git a/sql/item_row.h b/sql/item_row.h index 5b67ea8ce6c..73b198625ce 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -56,6 +56,11 @@ public: 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 *) diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 2ec7e3c338f..4a951896135 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1237,9 +1237,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)); diff --git a/sql/item_sum.h b/sql/item_sum.h index 1ed3d870bcc..96f115357f9 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -510,7 +510,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); @@ -1377,6 +1382,11 @@ public: 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) { diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 0b9e515a61f..ab41b496cde 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -187,6 +187,12 @@ public: nodeset->length(0); } enum Type type() const { return 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(); @@ -592,7 +598,12 @@ public: { 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]; diff --git a/sql/procedure.h b/sql/procedure.h index 1ece31223ad..8826b397fb2 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; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 392aa52825e..109f4124ce3 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -4080,9 +4080,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_select.cc b/sql/sql_select.cc index f1df24ee110..0eecb23a9c3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16525,60 +16525,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; @@ -16619,59 +16565,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 - @param convert_blob_length If >0 create a varstring(convert_blob_length) - field instead of blob. - - @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. @@ -16709,19 +16602,206 @@ 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(&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; +} + + +Field *Item_basic_value::create_tmp_field_ex(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param) +{ + /* + 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; + */ + DBUG_ASSERT(!param->make_copy_field()); + DBUG_ASSERT(!is_result_field()); + Field *result; + if ((result= tmp_table_field_from_field_type(table))) + { + if (type() == Item::NULL_ITEM) // Item_null or Item_param + result->is_created_from_null_item= true; + } + 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 @@ -16730,172 +16810,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; - } - 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; } /* @@ -17202,7 +17138,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); @@ -17252,7 +17188,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. @@ -17266,7 +17202,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 && @@ -17280,7 +17216,6 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, */ item->marker == 4 || param->bit_fields_as_long, force_copy_fields); - if (!new_field) { if (thd->is_fatal_error) @@ -23466,7 +23401,7 @@ calc_group_buffer(JOIN *join,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; diff --git a/sql/sql_select.h b/sql/sql_select.h index a2c21e06bf2..95b2d34c631 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1804,10 +1804,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 */ @@ -2059,12 +2055,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 |