Skip to content

Commit 1dc9196

Browse files
author
Bharathy Satish
committed
Bug #21650559 MYSQLPUMP MAKES CORRUPTED DUMP WHEN TABLE HAS GENERATED COLUMN
Problem: mysqlpump does not generate correct INSERT sql statements in dump file for tables which have generated columns. This will lead to error during restore. Fix: For tables with generated columns mysqlpump first identifies all the generated columns before preparing the INSERT sql statement for any table. Then those generated columns are ignored in the column name list of INSERT sql so that these sql statements get executed correctly during restore.
1 parent 99556d0 commit 1dc9196

10 files changed

+175
-32
lines changed

client/dump/mysql_field.cc

+12-6
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,32 @@
1818
#include "mysql_field.h"
1919

2020
using namespace Mysql::Tools::Dump;
21+
using namespace std;
2122

2223
enum enum_field_types Mysql_field::get_type() const
2324
{
24-
return m_field.type;
25+
return m_type;
2526
}
2627

2728
uint Mysql_field::get_additional_flags() const
2829
{
29-
return m_field.flags;
30+
return m_flags;
3031
}
3132

3233
uint Mysql_field::get_character_set_nr() const
3334
{
34-
return m_field.charsetnr;
35+
return m_charsetnr;
3536
}
3637

3738
std::string Mysql_field::get_name() const
3839
{
39-
return m_field.name;
40+
return m_name;
4041
}
4142

42-
Mysql_field::Mysql_field(MYSQL_FIELD* field) : m_field(*field)
43-
{}
43+
Mysql_field::Mysql_field(MYSQL_FIELD* field)
44+
: m_name(field->name),
45+
m_charsetnr(field->charsetnr),
46+
m_flags(field->flags),
47+
m_type(field->type)
48+
{
49+
}

client/dump/mysql_field.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ class Mysql_field
4040
enum enum_field_types get_type() const;
4141

4242
private:
43-
MYSQL_FIELD m_field;
43+
std::string m_name;
44+
unsigned int m_charsetnr;
45+
unsigned int m_flags;
46+
enum enum_field_types m_type;
4447
};
4548

4649
}

client/dump/mysql_object_reader.cc

+36-6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
#include "mysql_object_reader.h"
19+
#include <boost/algorithm/string.hpp>
1920

2021
using namespace Mysql::Tools::Dump;
2122

@@ -24,6 +25,7 @@ void Mysql_object_reader::Rows_fetching_context::acquire_fields_information(
2425
{
2526
MYSQL_FIELD* fields= mysql_fetch_fields(mysql_result);
2627
uint field_count= mysql_num_fields(mysql_result);
28+
m_fields.reserve(field_count);
2729
for (uint i= 0; i < field_count; ++i)
2830
m_fields.push_back(Mysql_field(&fields[i]));
2931
}
@@ -56,11 +58,13 @@ int64 Mysql_object_reader::Rows_fetching_context::result_callback(
5658
}
5759

5860
Mysql_object_reader::Rows_fetching_context::Rows_fetching_context(
59-
Mysql_object_reader* parent, Item_processing_data* item_processing)
61+
Mysql_object_reader* parent, Item_processing_data* item_processing,
62+
bool has_generated_column)
6063
: m_parent(parent),
6164
m_item_processing(item_processing),
6265
m_row_group((Table*)item_processing
63-
->get_process_task_object()->get_related_db_object(), m_fields)
66+
->get_process_task_object()->get_related_db_object(), m_fields,
67+
has_generated_column)
6468
{
6569
m_row_group.m_rows.reserve(
6670
(size_t)m_parent->m_options->m_row_group_size);
@@ -75,18 +79,44 @@ void Mysql_object_reader::read_table_rows_task(
7579
Table_rows_dump_task* table_rows_dump_task,
7680
Item_processing_data* item_to_process)
7781
{
82+
bool has_generated_columns= 0;
7883
Mysql::Tools::Base::Mysql_query_runner* runner= this->get_runner();
7984
Table* table= table_rows_dump_task->get_related_table();
8085

86+
std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> columns;
87+
std::vector<std::string> field_names;
88+
89+
runner->run_query_store(
90+
"SELECT `COLUMN_NAME`, `EXTRA` FROM " +
91+
this->get_quoted_object_full_name("INFORMATION_SCHEMA", "COLUMNS") +
92+
"WHERE TABLE_SCHEMA ='" + runner->escape_string(table->get_schema()) +
93+
"' AND TABLE_NAME ='" + runner->escape_string(table->get_name()) + "'",
94+
&columns);
95+
96+
std::string column_names;
97+
for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>::iterator
98+
it= columns.begin(); it != columns.end(); ++it)
99+
{
100+
const Mysql::Tools::Base::Mysql_query_runner::Row& column_data= **it;
101+
if (column_data[1] == "STORED GENERATED" ||
102+
column_data[1] == "VIRTUAL GENERATED")
103+
has_generated_columns= 1;
104+
else
105+
column_names+= this->quote_name(column_data[0]) + ",";
106+
}
107+
/* remove last comma from column_names */
108+
column_names= boost::algorithm::replace_last_copy(column_names, ",", "");
109+
81110
Rows_fetching_context* row_fetching_context=
82-
new Rows_fetching_context(this, item_to_process);
111+
new Rows_fetching_context(this, item_to_process, has_generated_columns);
83112

84113
runner->run_query(
85-
"SELECT SQL_NO_CACHE * FROM " + this->get_quoted_object_full_name(table),
114+
"SELECT SQL_NO_CACHE " + column_names + " FROM " +
115+
this->get_quoted_object_full_name(table),
86116
new Mysql::Instance_callback<
87117
int64, const Mysql::Tools::Base::Mysql_query_runner::Row&,
88-
Rows_fetching_context>(
89-
row_fetching_context, &Rows_fetching_context::result_callback));
118+
Rows_fetching_context>(
119+
row_fetching_context, &Rows_fetching_context::result_callback));
90120

91121
row_fetching_context->process_buffer();
92122
if (row_fetching_context->is_all_rows_processed())

client/dump/mysql_object_reader.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class Mysql_object_reader
6060
{
6161
public:
6262
Rows_fetching_context(Mysql_object_reader* parent,
63-
Item_processing_data* item_processing);
63+
Item_processing_data* item_processing, bool has_generated_column);
6464

6565
int64 result_callback(
6666
const Mysql::Tools::Base::Mysql_query_runner::Row& row_data);

client/dump/row_group_dump_task.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ I_data_object* Row_group_dump_task::get_related_db_object() const
4242
}
4343

4444
Row_group_dump_task::Row_group_dump_task(Table* source_table,
45-
const std::vector<Mysql_field>& fields)
45+
const std::vector<Mysql_field>& fields,
46+
const bool has_generated_column)
4647
: m_source_table(source_table),
47-
m_fields(fields)
48+
m_fields(fields),
49+
m_has_generated_columns(has_generated_column)
4850
{}

client/dump/row_group_dump_task.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ class Row_group_dump_task : public Abstract_simple_dump_task
3535
{
3636
public:
3737
Row_group_dump_task(
38-
Table* source_table, const std::vector<Mysql_field>& fields);
38+
Table* source_table, const std::vector<Mysql_field>& fields,
39+
const bool has_generated_column);
3940

4041
virtual I_data_object* get_related_db_object() const;
4142

@@ -46,7 +47,7 @@ class Row_group_dump_task : public Abstract_simple_dump_task
4647
/**
4748
Returns a table the rows are contained in.
4849
*/
49-
Table* m_source_table;
50+
const Table* m_source_table;
5051
/**
5152
Contains all fields information.
5253
*/
@@ -55,6 +56,10 @@ class Row_group_dump_task : public Abstract_simple_dump_task
5556
Returns all rows.
5657
*/
5758
std::vector<Row*> m_rows;
59+
/**
60+
Contains generated/virtual fields.
61+
*/
62+
const bool m_has_generated_columns;
5863
};
5964

6065
}

client/dump/sql_formatter.cc

+10-13
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,13 @@ void Sql_formatter::format_row_group(Row_group_dump_task* row_group)
4242
row_data_length+= row->m_row_data.size_of_element(column) * 2 + 3;
4343
}
4444
}
45-
if (m_options->m_dump_column_names)
45+
if (m_options->m_dump_column_names || row_group->m_has_generated_columns)
4646
{
4747
row_data_length+= 3; // Space for enclosing parentheses and space.
48-
const std::vector<Field>& fields=
49-
row_group->m_source_table->get_fields();
50-
for (std::vector<Field>::const_iterator field_iterator= fields.begin();
51-
field_iterator != fields.end();
52-
++field_iterator)
48+
const std::vector<Mysql_field>& fields= row_group->m_fields;
49+
for (std::vector<Mysql_field>::const_iterator
50+
field_iterator= fields.begin(); field_iterator != fields.end();
51+
++field_iterator)
5352
{
5453
row_data_length+= field_iterator->get_name().size() * 2 + 3;
5554
}
@@ -73,13 +72,12 @@ void Sql_formatter::format_row_group(Row_group_dump_task* row_group)
7372
else
7473
row_string+= "INSERT INTO ";
7574
row_string+= this->get_quoted_object_full_name(row_group->m_source_table);
76-
if (m_options->m_dump_column_names)
75+
if (m_options->m_dump_column_names || row_group->m_has_generated_columns)
7776
{
7877
row_string+= " (";
79-
const std::vector<Field>& fields=
80-
row_group->m_source_table->get_fields();
81-
for (std::vector<Field>::const_iterator field_iterator= fields.begin();
82-
field_iterator != fields.end();
78+
const std::vector<Mysql_field>& fields= row_group->m_fields;
79+
for (std::vector<Mysql_field>::const_iterator
80+
field_iterator= fields.begin(); field_iterator != fields.end();
8381
++field_iterator)
8482
{
8583
if (field_iterator != fields.begin())
@@ -143,8 +141,7 @@ void Sql_formatter::format_row_group(Row_group_dump_task* row_group)
143141
{
144142
row_string+= "NULL";
145143
}
146-
else if (row_group->m_fields[column].get_type()
147-
== MYSQL_TYPE_DECIMAL)
144+
else if (row_group->m_fields[column].get_type() == MYSQL_TYPE_DECIMAL)
148145
{
149146
row_string+= '\'';
150147
row_string.append(column_data, column_length);

client/dump/thread_specific_connection_provider.cc

+6-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ Mysql::Tools::Base::Mysql_query_runner*
2929
{
3030
runner= this->create_new_runner(message_handler);
3131
runner->run_query("SET SQL_QUOTE_SHOW_CREATE= 1");
32+
/*
33+
Do not allow server to make any timezone converstion even if it
34+
has a different time zone set.
35+
*/
36+
runner->run_query("SET TIME_ZONE='+00:00'");
3237
m_runner.reset(runner);
3338
}
3439
// Deliver copy of original runner.
@@ -38,4 +43,4 @@ Mysql::Tools::Base::Mysql_query_runner*
3843
Thread_specific_connection_provider::Thread_specific_connection_provider(
3944
Mysql::Tools::Base::I_connection_factory* connection_factory)
4045
: Abstract_connection_provider(connection_factory)
41-
{}
46+
{}

mysql-test/r/mysqlpump_basic.result

+56
Original file line numberDiff line numberDiff line change
@@ -204,16 +204,22 @@ trig1
204204
trig2
205205
SELECT 1 FROM db1_basic.t4 WHERE name= 'disk_temptable_create_cost' AND last_update = @val1;
206206
1
207+
1
207208
SELECT 2 FROM db1_basic.t4 WHERE name= 'disk_temptable_row_cost' AND last_update = @val2;
208209
2
210+
2
209211
SELECT 3 FROM db1_basic.t4 WHERE name= 'key_compare_cost' AND last_update = @val3;
210212
3
213+
3
211214
SELECT 4 FROM db1_basic.t4 WHERE name= 'memory_temptable_create_cost' AND last_update = @val4;
212215
4
216+
4
213217
SELECT 5 FROM db1_basic.t4 WHERE name= 'memory_temptable_row_cost' AND last_update = @val5;
214218
5
219+
5
215220
SELECT 6 FROM db1_basic.t4 WHERE name= 'row_evaluate_cost' AND last_update = @val6;
216221
6
222+
6
217223
# test for skip-triggers
218224
DROP DATABASE db1_basic;
219225
SHOW TRIGGERS FROM db1_basic;
@@ -495,3 +501,53 @@ disk_temptable_row_cost
495501
SELECT * FROM t5 ORDER BY 1;
496502
id id2
497503
DROP DATABASE bug21644479;
504+
#
505+
# Bug #21650559 MYSQLPUMP MAKES CORRUPTED DUMP WHEN TABLE HAS GENERATED COLUMN
506+
#
507+
CREATE DATABASE bug21650559;
508+
USE bug21650559;
509+
CREATE TABLE t1 (pk INTEGER, a INTEGER, b INTEGER, c VARCHAR(16),
510+
sum INTEGER GENERATED ALWAYS AS (a+b),
511+
sub VARCHAR(4) GENERATED ALWAYS AS (SUBSTRING(c, 1, 4)),
512+
key k1(sum),
513+
key k2(sub)
514+
);
515+
CREATE TABLE t2 (pk INTEGER, a INTEGER, b INTEGER,
516+
sum INTEGER GENERATED ALWAYS AS (a+b),
517+
c VARCHAR(16),
518+
key k1(sum)
519+
);
520+
CREATE TABLE t3 (sum INTEGER GENERATED ALWAYS AS (a+(EXTRACT(YEAR from pk))),
521+
pk TIMESTAMP, a INTEGER,
522+
c VARCHAR(16)
523+
);
524+
INSERT INTO t1(pk, a, b, c) VALUES (1, 11, 12, 'oneone'), (2, 21, 22, 'twotwo');
525+
SELECT * FROM t1;
526+
pk a b c sum sub
527+
1 11 12 oneone 23 oneo
528+
2 21 22 twotwo 43 twot
529+
INSERT INTO t2(pk, a, b, c) VALUES (1, 11, 12, 'oneone'), (2, 21, 22, 'twotwo');
530+
SELECT * FROM t2;
531+
pk a b sum c
532+
1 11 12 23 oneone
533+
2 21 22 43 twotwo
534+
INSERT INTO t3(pk, a, c) VALUES ('2003-01-02 10:30:00.000123', 11, 12), ('2015-11-22 00:30:00', 21, 22);
535+
SELECT * FROM t3;
536+
sum pk a c
537+
2014 2003-01-02 10:30:00 11 12
538+
2036 2015-11-22 00:30:00 21 22
539+
DROP DATABASE bug21650559;
540+
USE bug21650559;
541+
SELECT * FROM t1;
542+
pk a b c sum sub
543+
1 11 12 oneone 23 oneo
544+
2 21 22 twotwo 43 twot
545+
SELECT * FROM t2;
546+
pk a b sum c
547+
1 11 12 23 oneone
548+
2 21 22 43 twotwo
549+
SELECT * FROM t3;
550+
sum pk a c
551+
2014 2003-01-02 10:30:00 11 12
552+
2036 2015-11-22 00:30:00 21 22
553+
DROP DATABASE bug21650559;

mysql-test/t/mysqlpump_basic.test

+39
Original file line numberDiff line numberDiff line change
@@ -438,3 +438,42 @@ DROP DATABASE bug21644479;
438438
--remove_file $MYSQLTEST_VARDIR/tmp/bug21644479_zlib.sql
439439
--remove_file $MYSQLTEST_VARDIR/tmp/bug21644479.lz4
440440
--remove_file $MYSQLTEST_VARDIR/tmp/bug21644479.zlib
441+
442+
--echo #
443+
--echo # Bug #21650559 MYSQLPUMP MAKES CORRUPTED DUMP WHEN TABLE HAS GENERATED COLUMN
444+
--echo #
445+
446+
CREATE DATABASE bug21650559;
447+
USE bug21650559;
448+
CREATE TABLE t1 (pk INTEGER, a INTEGER, b INTEGER, c VARCHAR(16),
449+
sum INTEGER GENERATED ALWAYS AS (a+b),
450+
sub VARCHAR(4) GENERATED ALWAYS AS (SUBSTRING(c, 1, 4)),
451+
key k1(sum),
452+
key k2(sub)
453+
);
454+
CREATE TABLE t2 (pk INTEGER, a INTEGER, b INTEGER,
455+
sum INTEGER GENERATED ALWAYS AS (a+b),
456+
c VARCHAR(16),
457+
key k1(sum)
458+
);
459+
CREATE TABLE t3 (sum INTEGER GENERATED ALWAYS AS (a+(EXTRACT(YEAR from pk))),
460+
pk TIMESTAMP, a INTEGER,
461+
c VARCHAR(16)
462+
);
463+
464+
INSERT INTO t1(pk, a, b, c) VALUES (1, 11, 12, 'oneone'), (2, 21, 22, 'twotwo');
465+
SELECT * FROM t1;
466+
INSERT INTO t2(pk, a, b, c) VALUES (1, 11, 12, 'oneone'), (2, 21, 22, 'twotwo');
467+
SELECT * FROM t2;
468+
INSERT INTO t3(pk, a, c) VALUES ('2003-01-02 10:30:00.000123', 11, 12), ('2015-11-22 00:30:00', 21, 22);
469+
SELECT * FROM t3;
470+
471+
--exec $MYSQL_PUMP --databases bug21650559 > $MYSQLTEST_VARDIR/tmp/bug21650559.sql
472+
DROP DATABASE bug21650559;
473+
--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/bug21650559.sql
474+
--remove_file $MYSQLTEST_VARDIR/tmp/bug21650559.sql
475+
USE bug21650559;
476+
SELECT * FROM t1;
477+
SELECT * FROM t2;
478+
SELECT * FROM t3;
479+
DROP DATABASE bug21650559;

0 commit comments

Comments
 (0)