Skip to content
Permalink
Browse files
Backporting MDEV-15597 Add class Load_data_outvar and avoid using Ite…
…m::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"
  • Loading branch information
abarkov committed Feb 23, 2019
1 parent 8036ad5 commit 80c3fd1
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 228 deletions.
@@ -614,3 +614,24 @@ SELECT * FROM t1;
a b
1
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;
@@ -0,0 +1 @@

@@ -700,3 +700,19 @@ TRUNCATE TABLE t1;
LOAD DATA INFILE '../../std_data/loaddata/mdev-15497.txt' INTO TABLE t1 FIELDS TERMINATED BY '';
SELECT * FROM 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)
{
reset(); // Do not use the DEFAULT value
if (fixed_format)
{
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
return false;
}
@@ -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)
{
Field_enumerator *fe= (Field_enumerator*)arg;
@@ -1866,6 +1866,20 @@ class Item: public Value_source,
{
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
optimization to avoid computing expensive expressions during this
@@ -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:
void set_field(Field *field);
@@ -2585,6 +2600,30 @@ class Item_field :public Item_ident
bool val_bool_result();
bool is_null_result();
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);
bool fix_fields(THD *, Item **);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
@@ -4379,6 +4418,10 @@ class Item_ref :public Item_ident
void cleanup();
Item_field *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; }

// 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)
}


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('@');
append_identifier(thd, str, name.str, name.length);
@@ -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
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;
user_var_entry *entry;
public:
Item_user_var_as_out_param(THD *thd, LEX_STRING a): Item(thd), name(a)
{ 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 */
enum Type type() const { return STRING_ITEM;}
double val_real();
@@ -2053,7 +2083,6 @@ class Item_user_var_as_out_param :public Item
my_decimal *val_decimal(my_decimal *decimal_buffer);
/* fix_fields() binds variable name with its entry structure */
bool fix_fields(THD *thd, Item **ref);
void print_for_load(THD *thd, String *str);
void set_null_value(CHARSET_INFO* cs);
void set_value(const char *str, uint length, CHARSET_INFO* cs);
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }

0 comments on commit 80c3fd1

Please sign in to comment.