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; }
......
...@@ -134,11 +134,10 @@ static bool wsrep_load_data_split(THD *thd, const TABLE *table, ...@@ -134,11 +134,10 @@ static bool wsrep_load_data_split(THD *thd, const TABLE *table,
#define WSREP_LOAD_DATA_SPLIT(thd,table,info) /* empty */ #define WSREP_LOAD_DATA_SPLIT(thd,table,info) /* empty */
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
class READ_INFO { class READ_INFO: public Load_data_param
{
File file; File file;
String data; /* Read buffer */ String data; /* Read buffer */
uint fixed_length; /* Length of the fixed length record */
uint max_length; /* Max length of row */
Term_string m_field_term; /* FIELDS TERMINATED BY 'string' */ Term_string m_field_term; /* FIELDS TERMINATED BY 'string' */
Term_string m_line_term; /* LINES TERMINATED BY 'string' */ Term_string m_line_term; /* LINES TERMINATED BY 'string' */
Term_string m_line_start; /* LINES STARTING BY 'string' */ Term_string m_line_start; /* LINES STARTING BY 'string' */
...@@ -191,7 +190,7 @@ class READ_INFO { ...@@ -191,7 +190,7 @@ class READ_INFO {
bool read_mbtail(String *str) bool read_mbtail(String *str)
{ {
int chlen; int chlen;
if ((chlen= my_charlen(read_charset, str->end() - 1, str->end())) == 1) if ((chlen= my_charlen(charset(), str->end() - 1, str->end())) == 1)
return false; // Single byte character found return false; // Single byte character found
for (uint32 length0= str->length() - 1 ; MY_CS_IS_TOOSMALL(chlen); ) for (uint32 length0= str->length() - 1 ; MY_CS_IS_TOOSMALL(chlen); )
{ {
...@@ -202,7 +201,7 @@ class READ_INFO { ...@@ -202,7 +201,7 @@ class READ_INFO {
return true; // EOF return true; // EOF
} }
str->append(chr); str->append(chr);
chlen= my_charlen(read_charset, str->ptr() + length0, str->end()); chlen= my_charlen(charset(), str->ptr() + length0, str->end());
if (chlen == MY_CS_ILSEQ) if (chlen == MY_CS_ILSEQ)
{ {
/** /**
...@@ -224,10 +223,9 @@ class READ_INFO { ...@@ -224,10 +223,9 @@ class READ_INFO {
bool error,line_cuted,found_null,enclosed; bool error,line_cuted,found_null,enclosed;
uchar *row_start, /* Found row starts here */ uchar *row_start, /* Found row starts here */
*row_end; /* Found row ends here */ *row_end; /* Found row ends here */
CHARSET_INFO *read_charset;
LOAD_FILE_IO_CACHE cache; LOAD_FILE_IO_CACHE cache;
READ_INFO(THD *thd, File file, uint tot_length, CHARSET_INFO *cs, READ_INFO(THD *thd, File file, const Load_data_param &param,
String &field_term,String &line_start,String &line_term, String &field_term,String &line_start,String &line_term,
String &enclosed,int escape,bool get_it_from_net, bool is_fifo); String &enclosed,int escape,bool get_it_from_net, bool is_fifo);
~READ_INFO(); ~READ_INFO();
...@@ -282,6 +280,31 @@ static bool write_execute_load_query_log_event(THD *, sql_exchange*, const ...@@ -282,6 +280,31 @@ static bool write_execute_load_query_log_event(THD *, sql_exchange*, const
char*, const char*, bool, enum enum_duplicates, bool, bool, int); char*, const char*, bool, enum enum_duplicates, bool, bool, int);
#endif /* EMBEDDED_LIBRARY */ #endif /* EMBEDDED_LIBRARY */
bool Load_data_param::add_outvar_field(THD *thd, const Field *field)
{
if (field->flags & BLOB_FLAG)
{
m_use_blobs= true;
m_fixed_length+= 256; // Will be extended if needed
}
else
m_fixed_length+= field->field_length;
return false;
}
bool Load_data_param::add_outvar_user_var(THD *thd)
{
if (m_is_fixed_length)
{
my_error(ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR, MYF(0));
return true;
}
return false;
}
/* /*
Execute LOAD DATA query Execute LOAD DATA query
...@@ -313,8 +336,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -313,8 +336,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
File file; File file;
TABLE *table= NULL; TABLE *table= NULL;
int error= 0; int error= 0;
String *field_term=ex->field_term,*escaped=ex->escaped;
String *enclosed=ex->enclosed;
bool is_fifo=0; bool is_fifo=0;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
killed_state killed_status; killed_state killed_status;
...@@ -343,7 +364,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -343,7 +364,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
read_file_from_client = 0; //server is always in the same process read_file_from_client = 0; //server is always in the same process
#endif #endif
if (escaped->length() > 1 || enclosed->length() > 1) if (ex->escaped->length() > 1 || ex->enclosed->length() > 1)
{ {
my_message(ER_WRONG_FIELD_TERMINATORS, my_message(ER_WRONG_FIELD_TERMINATORS,
ER_THD(thd, ER_WRONG_FIELD_TERMINATORS), ER_THD(thd, ER_WRONG_FIELD_TERMINATORS),
...@@ -352,8 +373,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -352,8 +373,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
} }
/* Report problems with non-ascii separators */ /* Report problems with non-ascii separators */
if (!escaped->is_ascii() || !enclosed->is_ascii() || if (!ex->escaped->is_ascii() || !ex->enclosed->is_ascii() ||
!field_term->is_ascii() || !ex->field_term->is_ascii() ||
!ex->line_term->is_ascii() || !ex->line_start->is_ascii()) !ex->line_term->is_ascii() || !ex->line_start->is_ascii())
{ {
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
...@@ -450,39 +471,21 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -450,39 +471,21 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table->prepare_triggers_for_insert_stmt_or_event(); table->prepare_triggers_for_insert_stmt_or_event();
table->mark_columns_needed_for_insert(); table->mark_columns_needed_for_insert();
uint tot_length=0; Load_data_param param(ex->cs ? ex->cs : thd->variables.collation_database,
bool use_blobs= 0, use_vars= 0; !ex->field_term->length() && !ex->enclosed->length());
List_iterator_fast<Item> it(fields_vars); List_iterator_fast<Item> it(fields_vars);
Item *item; Item *item;
while ((item= it++)) while ((item= it++))
{ {
Item *real_item= item->real_item(); const Load_data_outvar *var= item->get_load_data_outvar_or_error();
if (!var || var->load_data_add_outvar(thd, &param))
if (real_item->type() == Item::FIELD_ITEM) DBUG_RETURN(true);
{
Field *field= ((Item_field*)real_item)->field;
if (field->flags & BLOB_FLAG)
{
use_blobs= 1;
tot_length+= 256; // Will be extended if needed
}
else
tot_length+= field->field_length;
}
else if (item->type() == Item::STRING_ITEM)
use_vars= 1;
} }
if (use_blobs && !ex->line_term->length() && !field_term->length()) if (param.use_blobs() && !ex->line_term->length() && !ex->field_term->length())
{ {
my_message(ER_BLOBS_AND_NO_TERMINATED, my_message(ER_BLOBS_AND_NO_TERMINATED,
ER_THD(thd, ER_BLOBS_AND_NO_TERMINATED), ER_THD(thd, ER_BLOBS_AND_NO_TERMINATED), MYF(0));
MYF(0));
DBUG_RETURN(TRUE);
}
if (use_vars && !field_term->length() && !enclosed->length())
{
my_error(ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR, MYF(0));
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
...@@ -572,13 +575,13 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -572,13 +575,13 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
bzero((char*) &info,sizeof(info)); bzero((char*) &info,sizeof(info));
info.ignore= ignore; info.ignore= ignore;
info.handle_duplicates=handle_duplicates; info.handle_duplicates=handle_duplicates;
info.escape_char= (escaped->length() && (ex->escaped_given() || info.escape_char= (ex->escaped->length() && (ex->escaped_given() ||
!(thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))) !(thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)))
? (*escaped)[0] : INT_MAX; ? (*ex->escaped)[0] : INT_MAX;
READ_INFO read_info(thd, file, tot_length, READ_INFO read_info(thd, file, param,
ex->cs ? ex->cs : thd->variables.collation_database, *ex->field_term, *ex->line_start,
*field_term,*ex->line_start, *ex->line_term, *enclosed, *ex->line_term, *ex->enclosed,
info.escape_char, read_file_from_client, is_fifo); info.escape_char, read_file_from_client, is_fifo);
if (read_info.error) if (read_info.error)
{ {
...@@ -639,14 +642,14 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -639,14 +642,14 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
error= read_xml_field(thd, info, table_list, fields_vars, error= read_xml_field(thd, info, table_list, fields_vars,
set_fields, set_values, read_info, set_fields, set_values, read_info,
*(ex->line_term), skip_lines, ignore); *(ex->line_term), skip_lines, ignore);
else if (!field_term->length() && !enclosed->length()) else if (read_info.is_fixed_length())
error= read_fixed_length(thd, info, table_list, fields_vars, error= read_fixed_length(thd, info, table_list, fields_vars,
set_fields, set_values, read_info, set_fields, set_values, read_info,
skip_lines, ignore); skip_lines, ignore);
else else
error= read_sep_field(thd, info, table_list, fields_vars, error= read_sep_field(thd, info, table_list, fields_vars,
set_fields, set_values, read_info, set_fields, set_values, read_info,
*enclosed, skip_lines, ignore); *ex->enclosed, skip_lines, ignore);
thd_proc_info(thd, "End bulk insert"); thd_proc_info(thd, "End bulk insert");
if (!error) if (!error)
...@@ -850,14 +853,9 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, ...@@ -850,14 +853,9 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
{ {
if (n++) if (n++)
query_str.append(", "); query_str.append(", ");
if (item->real_type() == Item::FIELD_ITEM) const Load_data_outvar *var= item->get_load_data_outvar();
append_identifier(thd, &query_str, item->name, strlen(item->name)); DBUG_ASSERT(var);
else var->load_data_print_for_log_event(thd, &query_str);
{
/* Actually Item_user_var_as_out_param despite claiming STRING_ITEM. */
DBUG_ASSERT(item->type() == Item::STRING_ITEM);
((Item_user_var_as_out_param *)item)->print_for_load(thd, &query_str);
}
} }
query_str.append(")"); query_str.append(")");
} }
...@@ -905,9 +903,9 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -905,9 +903,9 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
ulong skip_lines, bool ignore_check_option_errors) ulong skip_lines, bool ignore_check_option_errors)
{ {
List_iterator_fast<Item> it(fields_vars); List_iterator_fast<Item> it(fields_vars);
Item_field *sql_field; Item *item;
TABLE *table= table_list->table; TABLE *table= table_list->table;
bool err, progress_reports, auto_increment_field_not_null=false; bool err, progress_reports;
ulonglong counter, time_to_report_progress; ulonglong counter, time_to_report_progress;
DBUG_ENTER("read_fixed_length"); DBUG_ENTER("read_fixed_length");
...@@ -917,12 +915,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -917,12 +915,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if ((thd->progress.max_counter= read_info.file_length()) == ~(my_off_t) 0) if ((thd->progress.max_counter= read_info.file_length()) == ~(my_off_t) 0)
progress_reports= 0; progress_reports= 0;
while ((sql_field= (Item_field*) it++))
{
if (sql_field->field == table->next_number_field)
auto_increment_field_not_null= true;
}
while (!read_info.read_fixed_length()) while (!read_info.read_fixed_length())
{ {
if (thd->killed) if (thd->killed)
...@@ -958,36 +950,27 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -958,36 +950,27 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
#endif #endif
restore_record(table, s->default_values); restore_record(table, s->default_values);
/*
There is no variables in fields_vars list in this format so while ((item= it++))
this conversion is safe.
*/
while ((sql_field= (Item_field*) it++))
{ {
Field *field= sql_field->field; Load_data_outvar *dst= item->get_load_data_outvar();
table->auto_increment_field_not_null= auto_increment_field_not_null; DBUG_ASSERT(dst);
if (pos == read_info.row_end) if (pos == read_info.row_end)
{ {
if (field->load_data_set_no_data(thd, true)) if (dst->load_data_set_no_data(thd, &read_info))
DBUG_RETURN(1); DBUG_RETURN(1);
thd->cuted_fields++; /* Not enough 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());
} }
else else
{ {
uint length; uint length, fixed_length= dst->load_data_fixed_length();
uchar save_chr; uchar save_chr;
if ((length=(uint) (read_info.row_end-pos)) > if ((length=(uint) (read_info.row_end - pos)) > fixed_length)
field->field_length) length= fixed_length;
length=field->field_length; save_chr= pos[length]; pos[length]= '\0'; // Safeguard aganst malloc
save_chr=pos[length]; pos[length]='\0'; // Safeguard aganst malloc dst->load_data_set_value(thd, (const char *) pos, length, &read_info);
field->load_data_set_value((char*) pos,length,read_info.read_charset); pos[length]= save_chr;
pos[length]=save_chr; if ((pos+= length) > read_info.row_end)
if ((pos+=length) > read_info.row_end) pos= read_info.row_end; // Fills rest with space
pos= read_info.row_end; /* Fills rest with space */
} }
} }
if (pos != read_info.row_end) if (pos != read_info.row_end)
...@@ -1087,8 +1070,6 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1087,8 +1070,6 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
{ {
uint length; uint length;
uchar *pos; uchar *pos;
Item_field *real_item;
if (read_info.read_field()) if (read_info.read_field())
break; break;
...@@ -1099,48 +1080,22 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1099,48 +1080,22 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
pos=read_info.row_start; pos=read_info.row_start;
length=(uint) (read_info.row_end-pos); length=(uint) (read_info.row_end-pos);
real_item= item->field_for_view_update(); Load_data_outvar *dst= item->get_load_data_outvar_or_error();
DBUG_ASSERT(dst);
if ((!read_info.enclosed && if ((!read_info.enclosed &&
(enclosed_length && length == 4 && (enclosed_length && length == 4 &&
!memcmp(pos, STRING_WITH_LEN("NULL")))) || !memcmp(pos, STRING_WITH_LEN("NULL")))) ||
(length == 1 && read_info.found_null)) (length == 1 && read_info.found_null))
{ {
if (item->type() == Item::STRING_ITEM) if (dst->load_data_set_null(thd, &read_info))
{
((Item_user_var_as_out_param *)item)->set_null_value(
read_info.read_charset);
}
else if (!real_item)
{
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
DBUG_RETURN(1); DBUG_RETURN(1);
}
else
{
DBUG_ASSERT(real_item->field->table == table);
if (real_item->field->load_data_set_null(thd))
DBUG_RETURN(1);
}
continue;
}
if (item->type() == Item::STRING_ITEM)
{
((Item_user_var_as_out_param *)item)->set_value((char*) pos, length,
read_info.read_charset);
}
else if (!real_item)
{
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
DBUG_RETURN(1);
} }
else else
{ {
Field *field= real_item->field; read_info.row_end[0]= 0; // Safe to change end marker
read_info.row_end[0]=0; // Safe to change end marker if (dst->load_data_set_value(thd, (const char *) pos, length, &read_info))
field->load_data_set_value((char*) pos, length, read_info.read_charset); DBUG_RETURN(1);
} }
} }
...@@ -1161,34 +1116,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1161,34 +1116,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
break; break;
for (; item ; item= it++) for (; item ; item= it++)
{ {
Item_field *real_item= item->field_for_view_update(); Load_data_outvar *dst= item->get_load_data_outvar_or_error();
if (item->type() == Item::STRING_ITEM) DBUG_ASSERT(dst);
{ if (dst->load_data_set_no_data(thd, &read_info))
((Item_user_var_as_out_param *)item)->set_null_value(
read_info.read_charset);
}
else if (!real_item)
{
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
DBUG_RETURN(1); DBUG_RETURN(1);
}
else
{
Field *field= real_item->field;
if (field->load_data_set_no_data(thd, false))
DBUG_RETURN(1);
/*
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());
}
} }
} }
...@@ -1250,7 +1181,6 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1250,7 +1181,6 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
Item *item; Item *item;
TABLE *table= table_list->table; TABLE *table= table_list->table;
bool no_trans_update_stmt; bool no_trans_update_stmt;
CHARSET_INFO *cs= read_info.read_charset;
DBUG_ENTER("read_xml_field"); DBUG_ENTER("read_xml_field");
no_trans_update_stmt= !table->file->has_transactions(); no_trans_update_stmt= !table->file->has_transactions();
...@@ -1296,41 +1226,14 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1296,41 +1226,14 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
while(tag && strcmp(tag->field.c_ptr(), item->name) != 0) while(tag && strcmp(tag->field.c_ptr(), item->name) != 0)
tag= xmlit++; tag= xmlit++;
Item_field *real_item= item->field_for_view_update();
if (!tag) // found null
{
if (item->type() == Item::STRING_ITEM)
((Item_user_var_as_out_param *) item)->set_null_value(cs);
else if (!real_item)
{
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
DBUG_RETURN(1);
}
else
{
DBUG_ASSERT(real_item->field->table == table);
if (real_item->field->load_data_set_null(thd))
DBUG_RETURN(1);
}
continue;
}
if (item->type() == Item::STRING_ITEM) Load_data_outvar *dst= item->get_load_data_outvar_or_error();
((Item_user_var_as_out_param *) item)->set_value( DBUG_ASSERT(dst);
(char *) tag->value.ptr(), if (!tag ? dst->load_data_set_null(thd, &read_info) :
tag->value.length(), cs); dst->load_data_set_value(thd, tag->value.ptr(),
else if (!real_item) tag->value.length(),
{ &read_info))
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
DBUG_RETURN(1); DBUG_RETURN(1);
}
else
{
Field *field= ((Item_field *)item)->field;
field->load_data_set_value(tag->value.ptr(), tag->value.length(), cs);
}
} }
if (read_info.error) if (read_info.error)
...@@ -1341,39 +1244,8 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1341,39 +1244,8 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
skip_lines--; skip_lines--;
continue; continue;
} }
if (item) DBUG_ASSERT(!item);
{
/* Have not read any field, thus input file is simply ended */
if (item == fields_vars.head())
break;
for ( ; item; item= it++)
{
Item_field *real_item= item->field_for_view_update();
if (item->type() == Item::STRING_ITEM)
((Item_user_var_as_out_param *)item)->set_null_value(cs);
else if (!real_item)
{
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
DBUG_RETURN(1);
}
else
{
/*
QQ: 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());
}
}
}
if (thd->killed || if (thd->killed ||
fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values, fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values,
...@@ -1435,14 +1307,16 @@ READ_INFO::unescape(char chr) ...@@ -1435,14 +1307,16 @@ READ_INFO::unescape(char chr)
*/ */
READ_INFO::READ_INFO(THD *thd, File file_par, uint tot_length, CHARSET_INFO *cs, READ_INFO::READ_INFO(THD *thd, File file_par,
const Load_data_param &param,
String &field_term, String &line_start, String &line_term, String &field_term, String &line_start, String &line_term,
String &enclosed_par, int escape, bool get_it_from_net, String &enclosed_par, int escape, bool get_it_from_net,
bool is_fifo) bool is_fifo)
:file(file_par), fixed_length(tot_length), :Load_data_param(param),
file(file_par),
m_field_term(field_term), m_line_term(line_term), m_line_start(line_start), m_field_term(field_term), m_line_term(line_term), m_line_start(line_start),
escape_char(escape), found_end_of_line(false), eof(false), escape_char(escape), found_end_of_line(false), eof(false),
error(false), line_cuted(false), found_null(false), read_charset(cs) error(false), line_cuted(false), found_null(false)
{ {
data.set_thread_specific(); data.set_thread_specific();
/* /*
...@@ -1459,12 +1333,13 @@ READ_INFO::READ_INFO(THD *thd, File file_par, uint tot_length, CHARSET_INFO *cs, ...@@ -1459,12 +1333,13 @@ READ_INFO::READ_INFO(THD *thd, File file_par, uint tot_length, CHARSET_INFO *cs,
enclosed_char= enclosed_par.length() ? (uchar) enclosed_par[0] : INT_MAX; enclosed_char= enclosed_par.length() ? (uchar) enclosed_par[0] : INT_MAX;
/* Set of a stack for unget if long terminators */ /* Set of a stack for unget if long terminators */
uint length= MY_MAX(cs->mbmaxlen, MY_MAX(m_field_term.length(), uint length= MY_MAX(charset()->mbmaxlen, MY_MAX(m_field_term.length(),
m_line_term.length())) + 1; m_line_term.length())) + 1;
set_if_bigger(length,line_start.length()); set_if_bigger(length,line_start.length());
stack= stack_pos= (int*) thd->alloc(sizeof(int) * length); stack= stack_pos= (int*) thd->alloc(sizeof(int) * length);
if (data.reserve(tot_length)) DBUG_ASSERT(m_fixed_length < UINT_MAX32);
if (data.reserve((size_t) m_fixed_length))
error=1; /* purecov: inspected */ error=1; /* purecov: inspected */
else else
{ {
...@@ -1606,7 +1481,7 @@ int READ_INFO::read_field() ...@@ -1606,7 +1481,7 @@ int READ_INFO::read_field()
for (;;) for (;;)
{ {
// Make sure we have enough space for the longest multi-byte character. // Make sure we have enough space for the longest multi-byte character.
while (data.length() + read_charset->mbmaxlen <= data.alloced_length()) while (data.length() + charset()->mbmaxlen <= data.alloced_length())
{ {
chr = GET; chr = GET;
if (chr == my_b_EOF) if (chr == my_b_EOF)
...@@ -1692,7 +1567,7 @@ int READ_INFO::read_field() ...@@ -1692,7 +1567,7 @@ int READ_INFO::read_field()
} }
} }
data.append(chr); data.append(chr);
if (use_mb(read_charset) && read_mbtail(&data)) if (use_mb(charset()) && read_mbtail(&data))
goto found_eof; goto found_eof;
} }
/* /*
...@@ -1738,7 +1613,7 @@ int READ_INFO::read_fixed_length() ...@@ -1738,7 +1613,7 @@ int READ_INFO::read_fixed_length()
return 1; return 1;
} }
for (data.length(0); data.length() < fixed_length ; ) for (data.length(0); data.length() < m_fixed_length ; )
{ {
if ((chr=GET) == my_b_EOF) if ((chr=GET) == my_b_EOF)
goto found_eof; goto found_eof;
...@@ -1791,8 +1666,8 @@ int READ_INFO::next_line() ...@@ -1791,8 +1666,8 @@ int READ_INFO::next_line()
if (getbyte(&buf[0])) if (getbyte(&buf[0]))
return 1; // EOF return 1; // EOF
if (use_mb(read_charset) && if (use_mb(charset()) &&
(chlen= my_charlen(read_charset, buf, buf + 1)) != 1) (chlen= my_charlen(charset(), buf, buf + 1)) != 1)
{ {
uint i; uint i;
for (i= 1; MY_CS_IS_TOOSMALL(chlen); ) for (i= 1; MY_CS_IS_TOOSMALL(chlen); )
...@@ -1801,7 +1676,7 @@ int READ_INFO::next_line() ...@@ -1801,7 +1676,7 @@ int READ_INFO::next_line()
DBUG_ASSERT(chlen != 1); DBUG_ASSERT(chlen != 1);
if (getbyte(&buf[i++])) if (getbyte(&buf[i++]))
return 1; // EOF return 1; // EOF
chlen= my_charlen(read_charset, buf, buf + i); chlen= my_charlen(charset(), buf, buf + i);
} }
/* /*
...@@ -1972,7 +1847,7 @@ int READ_INFO::read_value(int delim, String *val) ...@@ -1972,7 +1847,7 @@ int READ_INFO::read_value(int delim, String *val)
else else
{ {
val->append(chr); val->append(chr);
if (use_mb(read_charset) && read_mbtail(val)) if (use_mb(charset()) && read_mbtail(val))
return my_b_EOF; return my_b_EOF;
} }
} }
......
...@@ -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