Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MDEV-36297: Ability to backup selected partitions only #3891

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 166 additions & 8 deletions client/mysqldump.cc
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ static my_bool verbose= 0, opt_no_create_info= 0, opt_no_data= 0, opt_no_data_m
opt_events= 0, opt_comments_used= 0,
opt_alltspcs=0, opt_notspcs= 0, opt_logging,
opt_header=0, opt_update_history= 0,
opt_drop_trigger= 0, opt_dump_history= 0;
opt_drop_trigger= 0, opt_dump_history= 0, opt_partitions = 0;
#define OPT_SYSTEM_ALL 1
#define OPT_SYSTEM_USERS 2
#define OPT_SYSTEM_PLUGINS 4
Expand Down Expand Up @@ -270,6 +270,13 @@ static struct my_option my_long_options[] =
{"no-tablespaces", 'y',
"Do not dump any tablespace information.",
&opt_notspcs, &opt_notspcs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"partitions", 0,
"Allows the user to specify partition in table names, please use "
"table_name#partition_name format. Double hash to escape # character. "
"Please add multiple entries for a single table to select multiple partitions. "
"These will be merged and produce a single table with selected "
"partitions in the output.",
&opt_partitions, &opt_partitions, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0 ,0, 0},
{"add-drop-database", 0, "Add a DROP DATABASE before each create.",
&opt_drop_database, &opt_drop_database, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
0},
Expand Down Expand Up @@ -1344,6 +1351,11 @@ static int get_options(int *argc, char ***argv)
my_progname_short);
return(EX_USAGE);
}
if (opt_databases && opt_partitions)
{
fprintf(stderr, "%s: --databases and --partitions can't be used together.\n", my_progname_short);
return(EX_USAGE);
}
if (ignore_database.records && !opt_alldbs)
{
fprintf(stderr,
Expand Down Expand Up @@ -4200,7 +4212,7 @@ static void send_query_completion_func(MYSQL* mysql, const char* query,
*/


static void dump_table(const char *table, const char *db, const uchar *hash_key, size_t len)
static void dump_table(const char *table, const char *db, const uchar *hash_key, size_t len, const char * partition = NULL)
{
char ignore_flag;
char buf[200], table_buff[NAME_LEN+3];
Expand Down Expand Up @@ -4351,6 +4363,11 @@ static void dump_table(const char *table, const char *db, const uchar *hash_key,
dynstr_append_checked(&query_string, qdatabase);
dynstr_append_checked(&query_string, ".");
dynstr_append_checked(&query_string, result_table);
if (partition != NULL) {
dynstr_append_checked(&query_string, " PARTITION (");
dynstr_append_checked(&query_string, partition);
dynstr_append_checked(&query_string, ") ");
}

if (versioned)
vers_append_system_time(&query_string);
Expand Down Expand Up @@ -4391,14 +4408,29 @@ static void dump_table(const char *table, const char *db, const uchar *hash_key,
}
else
{
print_comment(md_result_file, 0,
"\n--\n-- Dumping data for table %s\n--\n",
fix_for_comment(result_table));
if (partition == NULL)
{
print_comment(md_result_file, 0,
"\n--\n-- Dumping data for table %s\n--\n",
fix_for_comment(result_table));
}
else
{
print_comment(md_result_file, 0,
"\n--\n-- Dumping data for table %s, partition %s\n--\n",
fix_for_comment(result_table), partition);
}

dynstr_append_checked(&query_string, "SELECT /*!40001 SQL_NO_CACHE */ ");
dynstr_append_checked(&query_string, select_field_names.str);
dynstr_append_checked(&query_string, " FROM ");
dynstr_append_checked(&query_string, result_table);
if (partition != NULL) {
dynstr_append_checked(&query_string, " PARTITION (");
dynstr_append_checked(&query_string, partition);
dynstr_append_checked(&query_string, ") ");
}

if (versioned)
vers_append_system_time(&query_string);

Expand Down Expand Up @@ -6130,7 +6162,50 @@ static int get_sys_var_lower_case_table_names()
return lower_case_table_names;
}

// Returns partition name from table name. NULL if no partition is specified.
// Will put \0 in place of partition separator, so after return
// *tablename will contain only table name.
//
// Will treat double separator as escape in table name
// Will replace backtick in partition name with double backtick
// You need to do my_free on the result, if not NULL
static char* get_partition_from_table_name(char *tablename, const char separator = '#')
{
size_t namelen = strlen(tablename);
int jj = 0;
for (size_t ii=0; ii<namelen; ii++)
{
if (tablename[ii] != separator)
{
tablename[jj++] = tablename[ii];
continue;
}

if (ii+1 < namelen && tablename[ii+1] == separator)
{
// we found escaped (table##name) hash, put # in table name and move forward
ii++;
tablename[jj++] = separator;
}
else
{
// we found partition name separator, next char is the partition name
tablename[jj++] = 0x0;
ii=ii+1;

jj=0;
char *tmp = (char*) my_malloc(PSI_NOT_INSTRUMENTED, (strlen(&tablename[ii])*2+1) * sizeof(char), MYF(MY_WME | MY_ZEROFILL));
for (; tablename[ii] != 0; ii++) {
tmp[jj++] = tablename[ii];
if (tablename[ii] == '`')
tmp[jj++] = '`';
}
return tmp;
}
}

return NULL;
}

static int dump_selected_tables(char *db, char **table_names, int tables)
{
Expand All @@ -6140,6 +6215,69 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
int lower_case_table_names;
DBUG_ENTER("dump_selected_tables");

// remove partition names from table names, if we have separator defined
// put all partition names into **partition names variable, NULL = none
DYNAMIC_STRING **partition_names = NULL;
if (opt_partitions && tables > 0)
{
char ** new_table_names = (char **)my_malloc(PSI_NOT_INSTRUMENTED, 5 * sizeof (char *), MYF(MY_WME));
partition_names = (DYNAMIC_STRING **) my_malloc(PSI_NOT_INSTRUMENTED, tables * sizeof (DYNAMIC_STRING **), MYF(MY_WME));

for (int i=0; i<tables; i++)
{
new_table_names[i] = NULL;
partition_names[i] = NULL;
}

for (int i=0; i<tables; i++)
{
char *tname = table_names[i];
char *pname = get_partition_from_table_name(table_names[i]);
int tpos = 0;

// maybe we have this table added already
for (int j=0; j<tables; j++)
{
tpos = j;
if (new_table_names[j] == NULL || cmp_table(new_table_names[j], tname) == 0)
break;
}
new_table_names[tpos] = tname;

if (pname == NULL)
continue;
if (strlen(pname) == 0)
{
maybe_die(EX_USAGE, "Partition name can't be empty, please specify partition name for table %s", tname);
continue;
}

DYNAMIC_STRING *ppart = partition_names[tpos];
if (ppart == NULL) {
ppart = (DYNAMIC_STRING*) my_malloc(PSI_NOT_INSTRUMENTED, sizeof(DYNAMIC_STRING), MYF(MY_WME | MY_ZEROFILL));
init_dynamic_string_checked(ppart, "", 256, 1024);
partition_names[tpos] = ppart;
}
else
dynstr_append_checked(ppart, ", ");
dynstr_append_checked(ppart, "`");
dynstr_append_checked(ppart, pname);
dynstr_append_checked(ppart, "`");
}

for (int i=0; i<tables; i++)
table_names[i] = new_table_names[i];
for (int i=0; i<tables; i++)
{
if (table_names[i] == NULL)
{
tables = i;
break;
}
}
my_free(new_table_names);
}

if (init_dumping(db, init_dumping_tables))
DBUG_RETURN(1);

Expand All @@ -6155,7 +6293,9 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
lower_case_table_names = get_sys_var_lower_case_table_names();

init_dynamic_string_checked(&lock_tables_query, "LOCK TABLES ", 256, 1024);
for (; tables > 0 ; tables-- , table_names++)

DYNAMIC_STRING ** partition_names_pos = partition_names;
for (int table_num=0; table_num<tables; table_num++, table_names++)
{
/* the table name passed on commandline may be wrong case */
if ((*pos= get_actual_table_name(*table_names, lower_case_table_names,
Expand All @@ -6168,6 +6308,12 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
dynstr_append_checked(&lock_tables_query, " READ /*!32311 LOCAL */,");
}
pos++;

// move partition name, if we're skipping a table
if (partition_names != NULL) {
*partition_names_pos= partition_names[table_num];
partition_names_pos++;
}
}
else
{
Expand All @@ -6181,6 +6327,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
}
}
end= pos;
partition_names_pos= partition_names;

/* Can't LOCK TABLES in I_S / P_S, so don't try. */
if (lock_tables &&
Expand Down Expand Up @@ -6248,10 +6395,21 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
/* Dump each selected table */
for (pos= dump_tables; pos < end; pos++)
{
char *partition = NULL;
if (partition_names_pos != NULL && *partition_names_pos != NULL) {
partition = (*partition_names_pos)->str;
partition_names_pos++;
}

if (check_if_ignore_table(*pos, table_type) & IGNORE_SEQUENCE_TABLE)
continue;
DBUG_PRINT("info",("Dumping table %s", *pos));
dump_table(*pos, db, NULL, 0);

if (partition == NULL)
DBUG_PRINT("info",("Dumping table %s", *pos));
else
DBUG_PRINT("info",("Dumping table %s, partition %s", *pos, partition));

dump_table(*pos, db, NULL, 0, partition);
if (opt_dump_triggers &&
mysql_get_server_version(mysql) >= 50009)
{
Expand Down