Commit 80c3fd18 authored by Alexander Barkov's avatar Alexander Barkov

Backporting MDEV-15597 Add class Load_data_outvar and avoid using...

Backporting MDEV-15597 Add class Load_data_outvar and avoid using Item::STRING_ITEM for Item_user_var_as_out_param detection

This is a part of "MDEV-18045 Backporting the MDEV-15497 changes to 10.2 branch"
parent 8036ad54
...@@ -614,3 +614,24 @@ SELECT * FROM t1; ...@@ -614,3 +614,24 @@ SELECT * FROM t1;
a b a b
1 1
DROP TABLE t1; DROP TABLE t1;
#
# MDEV-15597 Add class Load_data_outvar and avoid using Item::STRING_ITEM for Item_user_var_as_out_param detection
#
SET sql_mode=NO_AUTO_VALUE_ON_ZERO;
CREATE TABLE t1 (id integer not null auto_increment primary key);
LOAD DATA INFILE '../../std_data/loaddata/nl.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
Warnings:
Warning 1261 Row 1 doesn't contain data for all columns
SELECT * FROM t1;
id
0
DROP TABLE t1;
SET sql_mode='';
CREATE TABLE t1 (id integer not null auto_increment primary key);
LOAD DATA INFILE '../../std_data/loaddata/nl.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
Warnings:
Warning 1261 Row 1 doesn't contain data for all columns
SELECT * FROM t1;
id
1
DROP TABLE t1;
...@@ -700,3 +700,19 @@ TRUNCATE TABLE t1; ...@@ -700,3 +700,19 @@ TRUNCATE TABLE t1;
LOAD DATA INFILE '../../std_data/loaddata/mdev-15497.txt' INTO TABLE t1 FIELDS TERMINATED BY ''; LOAD DATA INFILE '../../std_data/loaddata/mdev-15497.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # MDEV-15597 Add class Load_data_outvar and avoid using Item::STRING_ITEM for Item_user_var_as_out_param detection
--echo #
SET sql_mode=NO_AUTO_VALUE_ON_ZERO;
CREATE TABLE t1 (id integer not null auto_increment primary key);
LOAD DATA INFILE '../../std_data/loaddata/nl.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
SELECT * FROM t1;
DROP TABLE t1;
SET sql_mode='';
CREATE TABLE t1 (id integer not null auto_increment primary key);
LOAD DATA INFILE '../../std_data/loaddata/nl.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
SELECT * FROM t1;
DROP TABLE t1;
...@@ -1375,7 +1375,25 @@ bool Field::load_data_set_no_data(THD *thd, bool fixed_format) ...@@ -1375,7 +1375,25 @@ bool Field::load_data_set_no_data(THD *thd, bool fixed_format)
{ {
reset(); // Do not use the DEFAULT value reset(); // Do not use the DEFAULT value
if (fixed_format) if (fixed_format)
{
set_notnull(); set_notnull();
/*
We're loading a fixed format file, e.g.:
LOAD DATA INFILE 't1.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
Suppose the file ended unexpectedly and no data was provided for an
auto-increment column in the current row.
Historically, if sql_mode=NO_AUTO_VALUE_ON_ZERO, then the column value
is set to 0 in such case (the next auto_increment value is not used).
This behaviour was introduced by the fix for "bug#12053" in mysql-4.1.
Note, loading a delimited file works differently:
"no data" is not converted to 0 on NO_AUTO_VALUE_ON_ZERO:
it's considered as equal to setting the column to NULL,
which is then replaced to the next auto_increment value.
This difference seems to be intentional.
*/
if (this == table->next_number_field)
table->auto_increment_field_not_null= true;
}
set_has_explicit_value(); // Do not auto-update this field set_has_explicit_value(); // Do not auto-update this field
return false; return false;
} }
......
...@@ -2645,6 +2645,31 @@ void Item_field::reset_field(Field *f) ...@@ -2645,6 +2645,31 @@ void Item_field::reset_field(Field *f)
} }
void Item_field::load_data_print_for_log_event(THD *thd, String *to) const
{
append_identifier(thd, to, name, (uint) strlen(name));
}
bool Item_field::load_data_set_no_data(THD *thd, const Load_data_param *param)
{
if (field->load_data_set_no_data(thd, param->is_fixed_length()))
return true;
/*
TODO: We probably should not throw warning for each field.
But how about intention to always have the same number
of warnings in THD::cuted_fields (and get rid of cuted_fields
in the end ?)
*/
thd->cuted_fields++;
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_FEW_RECORDS,
ER_THD(thd, ER_WARN_TOO_FEW_RECORDS),
thd->get_stmt_da()->current_row_for_warning());
return false;
}
bool Item_field::enumerate_field_refs_processor(void *arg) bool Item_field::enumerate_field_refs_processor(void *arg)
{ {
Field_enumerator *fe= (Field_enumerator*)arg; Field_enumerator *fe= (Field_enumerator*)arg;
......
...@@ -1866,6 +1866,20 @@ class Item: public Value_source, ...@@ -1866,6 +1866,20 @@ class Item: public Value_source,
{ {
return 0; return 0;
} }
virtual Load_data_outvar *get_load_data_outvar()
{
return 0;
}
Load_data_outvar *get_load_data_outvar_or_error()
{
Load_data_outvar *dst= get_load_data_outvar();
if (dst)
return dst;
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), name);
return NULL;
}
/** /**
Test whether an expression is expensive to compute. Used during Test whether an expression is expensive to compute. Used during
optimization to avoid computing expensive expressions during this optimization to avoid computing expensive expressions during this
...@@ -2538,7 +2552,8 @@ class Item_ident_for_show :public Item ...@@ -2538,7 +2552,8 @@ class Item_ident_for_show :public Item
}; };
class Item_field :public Item_ident class Item_field :public Item_ident,
public Load_data_outvar
{ {
protected: protected:
void set_field(Field *field); void set_field(Field *field);
...@@ -2585,6 +2600,30 @@ class Item_field :public Item_ident ...@@ -2585,6 +2600,30 @@ class Item_field :public Item_ident
bool val_bool_result(); bool val_bool_result();
bool is_null_result(); bool is_null_result();
bool send(Protocol *protocol, String *str_arg); bool send(Protocol *protocol, String *str_arg);
Load_data_outvar *get_load_data_outvar()
{
return this;
}
bool load_data_set_null(THD *thd, const Load_data_param *param)
{
return field->load_data_set_null(thd);
}
bool load_data_set_value(THD *thd, const char *pos, uint length,
const Load_data_param *param)
{
field->load_data_set_value(pos, length, param->charset());
return false;
}
bool load_data_set_no_data(THD *thd, const Load_data_param *param);
void load_data_print_for_log_event(THD *thd, String *to) const;
bool load_data_add_outvar(THD *thd, Load_data_param *param) const
{
return param->add_outvar_field(thd, field);
}
uint load_data_fixed_length() const
{
return field->field_length;
}
void reset_field(Field *f); void reset_field(Field *f);
bool fix_fields(THD *, Item **); bool fix_fields(THD *, Item **);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
...@@ -4379,6 +4418,10 @@ class Item_ref :public Item_ident ...@@ -4379,6 +4418,10 @@ class Item_ref :public Item_ident
void cleanup(); void cleanup();
Item_field *field_for_view_update() Item_field *field_for_view_update()
{ return (*ref)->field_for_view_update(); } { return (*ref)->field_for_view_update(); }
Load_data_outvar *get_load_data_outvar()
{
return (*ref)->get_load_data_outvar();
}
virtual Ref_Type ref_type() { return REF; } virtual Ref_Type ref_type() { return REF; }
// Row emulation: forwarding of ROW-related calls to ref // Row emulation: forwarding of ROW-related calls to ref
......
...@@ -5751,7 +5751,9 @@ my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer) ...@@ -5751,7 +5751,9 @@ my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer)
} }
void Item_user_var_as_out_param::print_for_load(THD *thd, String *str) void Item_user_var_as_out_param::load_data_print_for_log_event(THD *thd,
String *str)
const
{ {
str->append('@'); str->append('@');
append_identifier(thd, str, name.str, name.length); append_identifier(thd, str, name.str, name.length);
......
...@@ -2038,13 +2038,43 @@ class Item_func_get_user_var :public Item_func_user_var, ...@@ -2038,13 +2038,43 @@ class Item_func_get_user_var :public Item_func_user_var,
in List<Item> and desire to place this code somewhere near other functions in List<Item> and desire to place this code somewhere near other functions
working with user variables. working with user variables.
*/ */
class Item_user_var_as_out_param :public Item class Item_user_var_as_out_param :public Item,
public Load_data_outvar
{ {
LEX_STRING name; LEX_STRING name;
user_var_entry *entry; user_var_entry *entry;
public: public:
Item_user_var_as_out_param(THD *thd, LEX_STRING a): Item(thd), name(a) Item_user_var_as_out_param(THD *thd, LEX_STRING a): Item(thd), name(a)
{ set_name(thd, a.str, 0, system_charset_info); } { set_name(thd, a.str, 0, system_charset_info); }
Load_data_outvar *get_load_data_outvar()
{
return this;
}
bool load_data_set_null(THD *thd, const Load_data_param *param)
{
set_null_value(param->charset());
return false;
}
bool load_data_set_no_data(THD *thd, const Load_data_param *param)
{
set_null_value(param->charset());
return false;
}
bool load_data_set_value(THD *thd, const char *pos, uint length,
const Load_data_param *param)
{
set_value(pos, length, param->charset());
return false;
}
void load_data_print_for_log_event(THD *thd, String *to) const;
bool load_data_add_outvar(THD *thd, Load_data_param *param) const
{
return param->add_outvar_user_var(thd);
}
uint load_data_fixed_length() const
{
return 0;
}
/* We should return something different from FIELD_ITEM here */ /* We should return something different from FIELD_ITEM here */
enum Type type() const { return STRING_ITEM;} enum Type type() const { return STRING_ITEM;}
double val_real(); double val_real();
...@@ -2053,7 +2083,6 @@ class Item_user_var_as_out_param :public Item ...@@ -2053,7 +2083,6 @@ class Item_user_var_as_out_param :public Item
my_decimal *val_decimal(my_decimal *decimal_buffer); my_decimal *val_decimal(my_decimal *decimal_buffer);
/* fix_fields() binds variable name with its entry structure */ /* fix_fields() binds variable name with its entry structure */
bool fix_fields(THD *thd, Item **ref); bool fix_fields(THD *thd, Item **ref);
void print_for_load(THD *thd, String *str);
void set_null_value(CHARSET_INFO* cs); void set_null_value(CHARSET_INFO* cs);
void set_value(const char *str, uint length, CHARSET_INFO* cs); void set_value(const char *str, uint length, CHARSET_INFO* cs);
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
......
This diff is collapsed.
...@@ -629,4 +629,40 @@ struct Lex_dyncol_type_st: public Lex_length_and_dec_st ...@@ -629,4 +629,40 @@ struct Lex_dyncol_type_st: public Lex_length_and_dec_st
}; };
class Load_data_param
{
protected:
CHARSET_INFO *m_charset; // Character set of the file
ulonglong m_fixed_length; // Sum of target field lengths for fixed format
bool m_is_fixed_length;
bool m_use_blobs;
public:
Load_data_param(CHARSET_INFO *cs, bool is_fixed_length):
m_charset(cs),
m_fixed_length(0),
m_is_fixed_length(is_fixed_length),
m_use_blobs(false)
{ }
bool add_outvar_field(THD *thd, const Field *field);
bool add_outvar_user_var(THD *thd);
CHARSET_INFO *charset() const { return m_charset; }
bool is_fixed_length() const { return m_is_fixed_length; }
bool use_blobs() const { return m_use_blobs; }
};
class Load_data_outvar
{
public:
virtual ~Load_data_outvar() {}
virtual bool load_data_set_null(THD *thd, const Load_data_param *param)= 0;
virtual bool load_data_set_value(THD *thd, const char *pos, uint length,
const Load_data_param *param)= 0;
virtual bool load_data_set_no_data(THD *thd, const Load_data_param *param)= 0;
virtual void load_data_print_for_log_event(THD *thd, class String *to) const= 0;
virtual bool load_data_add_outvar(THD *thd, Load_data_param *param) const= 0;
virtual uint load_data_fixed_length() const= 0;
};
#endif /* STRUCTS_INCLUDED */ #endif /* STRUCTS_INCLUDED */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment