Skip to content

Commit 9eee9c6

Browse files
committed
MDEV-16355 Add option for mysqldump to read data as of specific timestamp from system-versioned tables
Based on Aleksey Midenkov's patch mysqldump changes: * --as-of option specifies historical point; * query forging protection for --as-of parameter. system versioned tables are detected by querying I_S.TABLES: * it transfers much less data when the full table definition is not needed * it does not give false positives on x TEXT DEFAULT 'WITH SYSTEM VERSIONING'
1 parent 0860b17 commit 9eee9c6

File tree

4 files changed

+133
-7
lines changed

4 files changed

+133
-7
lines changed

client/client_priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ enum options_client
104104
OPT_SHUTDOWN_WAIT_FOR_SLAVES,
105105
OPT_COPY_S3_TABLES,
106106
OPT_PRINT_TABLE_METADATA,
107+
OPT_ASOF_TIMESTAMP,
107108
OPT_MAX_CLIENT_OPTION /* should be always the last */
108109
};
109110

client/mysqldump.c

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ static my_bool verbose= 0, opt_no_create_info= 0, opt_no_data= 0, opt_no_data_m
129129
opt_include_master_host_port= 0,
130130
opt_events= 0, opt_comments_used= 0,
131131
opt_alltspcs=0, opt_notspcs= 0, opt_logging,
132-
opt_drop_trigger= 0 ;
132+
opt_drop_trigger= 0;
133133
#define OPT_SYSTEM_ALL 1
134134
#define OPT_SYSTEM_USERS 2
135135
#define OPT_SYSTEM_PLUGINS 4
@@ -155,7 +155,7 @@ static char *opt_password=0,*current_user=0,
155155
*lines_terminated=0, *enclosed=0, *opt_enclosed=0, *escaped=0,
156156
*where=0, *order_by=0,
157157
*err_ptr= 0,
158-
*log_error_file= NULL;
158+
*log_error_file= NULL, *opt_asof_timestamp= NULL;
159159
static const char *opt_compatible_mode_str= 0;
160160
static char **defaults_argv= 0;
161161
static char compatible_mode_normal_str[255];
@@ -278,6 +278,9 @@ static struct my_option my_long_options[] =
278278
"Adds 'STOP SLAVE' prior to 'CHANGE MASTER' and 'START SLAVE' to bottom of dump.",
279279
&opt_slave_apply, &opt_slave_apply, 0, GET_BOOL, NO_ARG,
280280
0, 0, 0, 0, 0, 0},
281+
{"as-of", OPT_ASOF_TIMESTAMP,
282+
"Dump system versioned table as of specified timestamp.",
283+
&opt_asof_timestamp, &opt_asof_timestamp, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
281284
{"character-sets-dir", OPT_CHARSETS_DIR,
282285
"Directory for character set files.", (char **)&charsets_dir,
283286
(char **)&charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
@@ -1325,6 +1328,12 @@ static int get_options(int *argc, char ***argv)
13251328
my_progname_short);
13261329
return(EX_USAGE);
13271330
}
1331+
if (opt_asof_timestamp && strchr(opt_asof_timestamp, '\''))
1332+
{
1333+
fprintf(stderr, "%s: Incorrect DATETIME value: '%s'\n",
1334+
my_progname_short, opt_asof_timestamp);
1335+
return(EX_USAGE);
1336+
}
13281337
if (strcmp(default_charset, MYSQL_AUTODETECT_CHARSET_NAME) &&
13291338
!(charset_info= get_charset_by_csname(default_charset,
13301339
MY_CS_PRIMARY,
@@ -3039,7 +3048,7 @@ static void get_sequence_structure(const char *seq, const char *db)
30393048
*/
30403049

30413050
static uint get_table_structure(const char *table, const char *db, char *table_type,
3042-
char *ignore_flag)
3051+
char *ignore_flag, my_bool *versioned)
30433052
{
30443053
my_bool init=0, delayed, write_data, complete_insert;
30453054
my_ulonglong num_fields;
@@ -3106,6 +3115,26 @@ static uint get_table_structure(const char *table, const char *db, char *table_t
31063115

31073116
verbose_msg("-- Retrieving table structure for table %s...\n", table);
31083117

3118+
if (versioned)
3119+
{
3120+
if (!opt_asof_timestamp)
3121+
versioned= NULL;
3122+
else
3123+
{
3124+
my_snprintf(query_buff, sizeof(query_buff), "select 1 from"
3125+
" information_schema.tables where table_schema=database()"
3126+
" and table_name=%s and table_type='SYSTEM VERSIONED'",
3127+
quote_for_equal(table, table_buff));
3128+
if (!mysql_query_with_error_report(mysql, &result, query_buff))
3129+
{
3130+
*versioned= result->row_count > 0;
3131+
mysql_free_result(result);
3132+
}
3133+
else
3134+
*versioned= 0;
3135+
}
3136+
}
3137+
31093138
len= my_snprintf(query_buff, sizeof(query_buff),
31103139
"SET SQL_QUOTE_SHOW_CREATE=%d",
31113140
(opt_quoted || opt_keywords));
@@ -3991,6 +4020,15 @@ static char *alloc_query_str(size_t size)
39914020
}
39924021

39934022

4023+
static void vers_append_system_time(DYNAMIC_STRING* query_string)
4024+
{
4025+
DBUG_ASSERT(opt_asof_timestamp);
4026+
dynstr_append_checked(query_string, " FOR SYSTEM_TIME AS OF TIMESTAMP '");
4027+
dynstr_append_checked(query_string, opt_asof_timestamp);
4028+
dynstr_append_checked(query_string, "'");
4029+
}
4030+
4031+
39944032
/*
39954033
39964034
SYNOPSIS
@@ -4018,6 +4056,7 @@ static void dump_table(const char *table, const char *db, const uchar *hash_key,
40184056
ulong rownr, row_break;
40194057
uint num_fields;
40204058
size_t total_length, init_length;
4059+
my_bool versioned= 0;
40214060

40224061
MYSQL_RES *res;
40234062
MYSQL_FIELD *field;
@@ -4028,7 +4067,7 @@ static void dump_table(const char *table, const char *db, const uchar *hash_key,
40284067
Make sure you get the create table info before the following check for
40294068
--no-data flag below. Otherwise, the create table info won't be printed.
40304069
*/
4031-
num_fields= get_table_structure(table, db, table_type, &ignore_flag);
4070+
num_fields= get_table_structure(table, db, table_type, &ignore_flag, &versioned);
40324071

40334072
/*
40344073
The "table" could be a view. If so, we don't do anything here.
@@ -4135,6 +4174,8 @@ static void dump_table(const char *table, const char *db, const uchar *hash_key,
41354174

41364175
dynstr_append_checked(&query_string, " FROM ");
41374176
dynstr_append_checked(&query_string, result_table);
4177+
if (versioned)
4178+
vers_append_system_time(&query_string);
41384179

41394180
if (where)
41404181
{
@@ -4167,6 +4208,8 @@ static void dump_table(const char *table, const char *db, const uchar *hash_key,
41674208
dynstr_append_checked(&query_string, select_field_names.str);
41684209
dynstr_append_checked(&query_string, " FROM ");
41694210
dynstr_append_checked(&query_string, result_table);
4211+
if (versioned)
4212+
vers_append_system_time(&query_string);
41704213

41714214
if (where)
41724215
{
@@ -5637,21 +5680,21 @@ static int dump_all_tables_in_db(char *database)
56375680
if (general_log_table_exists)
56385681
{
56395682
if (!get_table_structure((char *) "general_log",
5640-
database, table_type, &ignore_flag) )
5683+
database, table_type, &ignore_flag, NULL) )
56415684
verbose_msg("-- Warning: get_table_structure() failed with some internal "
56425685
"error for 'general_log' table\n");
56435686
}
56445687
if (slow_log_table_exists)
56455688
{
56465689
if (!get_table_structure((char *) "slow_log",
5647-
database, table_type, &ignore_flag) )
5690+
database, table_type, &ignore_flag, NULL) )
56485691
verbose_msg("-- Warning: get_table_structure() failed with some internal "
56495692
"error for 'slow_log' table\n");
56505693
}
56515694
if (transaction_registry_table_exists)
56525695
{
56535696
if (!get_table_structure((char *) "transaction_registry",
5654-
database, table_type, &ignore_flag) )
5697+
database, table_type, &ignore_flag, NULL) )
56555698
verbose_msg("-- Warning: get_table_structure() failed with some internal "
56565699
"error for 'transaction_registry' table\n");
56575700
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#
2+
# MDEV-16355 Add option for mysqldump to read data as of specific timestamp from system-versioned tables
3+
#
4+
create or replace table t1 (x int) with system versioning;
5+
set timestamp=unix_timestamp('1990-01-01 00:00');
6+
insert t1 (x) values (1),(2),(3);
7+
set timestamp=unix_timestamp('1990-08-03 00:00');
8+
delete from t1 where x=1;
9+
set timestamp=unix_timestamp('1991-01-02 00:00');
10+
delete from t1 where x=2;
11+
set timestamp=default;
12+
#MYSQL_DUMP --compact test
13+
/*!40101 SET @saved_cs_client = @@character_set_client */;
14+
/*!40101 SET character_set_client = utf8 */;
15+
CREATE TABLE `t1` (
16+
`x` int(11) DEFAULT NULL
17+
) ENGINE=MyISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING;
18+
/*!40101 SET character_set_client = @saved_cs_client */;
19+
INSERT INTO `t1` VALUES (3);
20+
#MYSQL_DUMP --compact --as-of="1990-01-02 00:00" test
21+
/*!40101 SET @saved_cs_client = @@character_set_client */;
22+
/*!40101 SET character_set_client = utf8 */;
23+
CREATE TABLE `t1` (
24+
`x` int(11) DEFAULT NULL
25+
) ENGINE=MyISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING;
26+
/*!40101 SET character_set_client = @saved_cs_client */;
27+
INSERT INTO `t1` VALUES (1),(2),(3);
28+
#MYSQL_DUMP --compact --as-of="1990-08-02 00:00" --databases test
29+
30+
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `test` /*!40100 DEFAULT CHARACTER SET latin1 */;
31+
32+
USE `test`;
33+
/*!40101 SET @saved_cs_client = @@character_set_client */;
34+
/*!40101 SET character_set_client = utf8 */;
35+
CREATE TABLE `t1` (
36+
`x` int(11) DEFAULT NULL
37+
) ENGINE=MyISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING;
38+
/*!40101 SET character_set_client = @saved_cs_client */;
39+
INSERT INTO `t1` VALUES (1),(2),(3);
40+
#MYSQL_DUMP --compact --as-of="1990-08-04 00:00" test t1
41+
/*!40101 SET @saved_cs_client = @@character_set_client */;
42+
/*!40101 SET character_set_client = utf8 */;
43+
CREATE TABLE `t1` (
44+
`x` int(11) DEFAULT NULL
45+
) ENGINE=MyISAM DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING;
46+
/*!40101 SET character_set_client = @saved_cs_client */;
47+
INSERT INTO `t1` VALUES (2),(3);
48+
#MYSQL_DUMP --compact --as-of="1990-08-04 00:00' where 'abc" test 2>&1
49+
mysqldump: Incorrect DATETIME value: '1990-08-04 00:00' where 'abc'
50+
drop tables t1;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--source include/not_embedded.inc
2+
--source suite/versioning/common.inc
3+
4+
--echo #
5+
--echo # MDEV-16355 Add option for mysqldump to read data as of specific timestamp from system-versioned tables
6+
--echo #
7+
create or replace table t1 (x int) with system versioning;
8+
set timestamp=unix_timestamp('1990-01-01 00:00');
9+
insert t1 (x) values (1),(2),(3);
10+
set timestamp=unix_timestamp('1990-08-03 00:00');
11+
delete from t1 where x=1;
12+
set timestamp=unix_timestamp('1991-01-02 00:00');
13+
delete from t1 where x=2;
14+
set timestamp=default;
15+
16+
--echo #MYSQL_DUMP --compact test
17+
--exec $MYSQL_DUMP --compact test
18+
--echo #MYSQL_DUMP --compact --as-of="1990-01-02 00:00" test
19+
--exec $MYSQL_DUMP --compact --as-of="1990-01-02 00:00" test
20+
--echo #MYSQL_DUMP --compact --as-of="1990-08-02 00:00" --databases test
21+
--exec $MYSQL_DUMP --compact --as-of="1990-08-02 00:00" --databases test
22+
--echo #MYSQL_DUMP --compact --as-of="1990-08-04 00:00" test t1
23+
--exec $MYSQL_DUMP --compact --as-of="1990-08-04 00:00" test t1
24+
## Forged query protection
25+
--echo #MYSQL_DUMP --compact --as-of="1990-08-04 00:00' where 'abc" test 2>&1
26+
--replace_result mysqldump.exe mysqldump
27+
--error 1
28+
--exec $MYSQL_DUMP --compact --as-of="1990-08-04 00:00' where 'abc" test 2>&1
29+
30+
drop tables t1;
31+
32+
--source suite/versioning/common_finish.inc

0 commit comments

Comments
 (0)