diff --git a/client/import_util.cc b/client/import_util.cc index baade1535c69d..47f39c913d173 100644 --- a/client/import_util.cc +++ b/client/import_util.cc @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -58,10 +59,18 @@ std::string extract_first_create_table(const std::string &script) TableDDLInfo::TableDDLInfo(const std::string &create_table_stmt) { regex_t primary_key_regex, constraint_regex, index_regex, engine_regex, - table_name_regex; + table_name_regex, column_regex; constexpr size_t MAX_MATCHES= 10; regmatch_t match[10]; + // Extract just the CREATE TABLE statement if the input contains other SQL + std::string actual_create_table = extract_first_create_table(create_table_stmt); + if (actual_create_table.empty()) + { + // Input might already be just the CREATE TABLE statement + actual_create_table = create_table_stmt; + } + regcomp(&primary_key_regex, "\\n\\s*(PRIMARY\\s+KEY\\s+(.*?)),?\\n", REG_EXTENDED); regcomp(&constraint_regex, @@ -73,8 +82,13 @@ TableDDLInfo::TableDDLInfo(const std::string &create_table_stmt) regcomp(&engine_regex, "\\bENGINE\\s*=\\s*(\\w+)", REG_EXTENDED); regcomp(&table_name_regex, "CREATE\\s+TABLE\\s+(`?(?:[^`]|``)+`?)\\s*\\(", REG_EXTENDED); + // Column regex: matches lines starting with column name followed by type + // Must be inside parentheses, not starting with constraint/key keywords + regcomp(&column_regex, + "\\n\\s*(`[^`]+`|[a-zA-Z_][a-zA-Z0-9_]*)\\s+([a-zA-Z]+[a-zA-Z0-9()]*)", + REG_EXTENDED); - const char *stmt= create_table_stmt.c_str(); + const char *stmt= actual_create_table.c_str(); const char *search_start= stmt; // Extract primary key @@ -129,11 +143,97 @@ TableDDLInfo::TableDDLInfo(const std::string &create_table_stmt) } } } + +// Extract column definitions - find the column definition block + const char *col_start = strchr(stmt, '('); + if (col_start) + { + col_start++; // Move past the opening '(' + + // Find the matching closing parenthesis + int depth = 1; + const char *col_end = col_start; + bool in_string = false; + char string_delim = 0; + + while (*col_end && depth > 0) + { + // Handle strings + if (!in_string && (*col_end == '\'' || *col_end == '"' || *col_end == '`')) + { + in_string = true; + string_delim = *col_end; + } + else if (in_string && *col_end == string_delim) + { + // Check for escaped delimiter + if (*(col_end + 1) == string_delim) + col_end++; // Skip escaped + else + in_string = false; + } + else if (!in_string) + { + if (*col_end == '(') + depth++; + else if (*col_end == ')') + { + depth--; + if (depth == 0) + break; // Found the matching closing paren + } + } + col_end++; + } + + if (depth == 0 && col_end > col_start) + { + // col_end now points to the closing ')' + std::string columns_block(col_start, col_end - col_start); + + // Now parse column definitions from this block only + // Pattern matches lines that start with whitespace + identifier + whitespace + type + regcomp(&column_regex, + "\\n[ \\t]*(`[^`]+`|[a-zA-Z_][a-zA-Z0-9_]*)[ \\t]+([a-zA-Z]+)", + REG_EXTENDED); + + const char *block = columns_block.c_str(); + search_start = block; + + while (regexec(&column_regex, search_start, MAX_MATCHES, match, 0) == 0) + { + std::string col_name(search_start + match[1].rm_so, + match[1].rm_eo - match[1].rm_so); + std::string col_type(search_start + match[2].rm_so, + match[2].rm_eo - match[2].rm_so); + + // Remove backticks + if (!col_name.empty() && col_name.front() == '`' && col_name.back() == '`') + col_name = col_name.substr(1, col_name.length() - 2); + + // Convert to uppercase for comparison + std::string col_name_upper = col_name; + for (char &c : col_name_upper) c = toupper(c); + + // Skip constraint/key lines + if (col_name_upper != "PRIMARY" && col_name_upper != "CONSTRAINT" && + col_name_upper != "KEY" && col_name_upper != "INDEX" && + col_name_upper != "UNIQUE" && col_name_upper != "FULLTEXT" && + col_name_upper != "SPATIAL" && col_name_upper != "VECTOR") + { + columns.push_back({col_name, col_type}); + } + + search_start += match[0].rm_eo - 1; + } + } + } regfree(&primary_key_regex); regfree(&constraint_regex); regfree(&index_regex); regfree(&engine_regex); regfree(&table_name_regex); + regfree(&column_regex); } /** diff --git a/client/import_util.h b/client/import_util.h index 27e4decc77fcd..13063dfe93fd0 100644 --- a/client/import_util.h +++ b/client/import_util.h @@ -40,6 +40,12 @@ struct KeyDefinition std::string name; }; +struct ColumnInfo +{ + std::string name; + std::string type; +}; + /** Information about keys and constraints, extracted from CREATE TABLE statement @@ -50,6 +56,7 @@ struct TableDDLInfo KeyDefinition primary_key; std::vector constraints; std::vector secondary_indexes; + std::vector columns; std::string storage_engine; std::string table_name; /* Innodb is using first UNIQUE key for clustering, if no PK is set*/ diff --git a/client/mysqldump.cc b/client/mysqldump.cc index fc5d990478e34..70e69e9b2bee1 100644 --- a/client/mysqldump.cc +++ b/client/mysqldump.cc @@ -3473,7 +3473,7 @@ static uint get_table_structure(const char *table, const char *db, char *table_t mysql_free_result(result); } my_snprintf(query_buff, sizeof(query_buff), - "select column_name, extra, generation_expression, data_type " + "select column_name, extra, generation_expression, data_type, character_set_name " "from information_schema.columns where table_schema=database() " "and table_name=%s order by ordinal_position", quote_for_equal(table, temp_buff)); @@ -3506,6 +3506,26 @@ static uint get_table_structure(const char *table, const char *db, char *table_t dynstr_append_checked(&select_field_names_for_header, ", "); } init=1; + my_bool is_blob_field= 0; + /* + Check if this is a binary/blob field that should be hex-encoded. + For multi_file_output (--tab/--dir), we need to wrap with HEX() in SELECT. + Binary fields have character_set_name = NULL or 'binary'. + */ + if (opt_hex_blob && multi_file_output && row[3]) + { + /* Check for blob/binary types with binary charset */ + if ((row[4] == NULL || strcmp(row[4], "binary") == 0) && + (strcmp(row[3], "binary") == 0 || + strcmp(row[3], "varbinary") == 0 || + strcmp(row[3], "tinyblob") == 0 || + strcmp(row[3], "blob") == 0 || + strcmp(row[3], "mediumblob") == 0 || + strcmp(row[3], "longblob") == 0)) + { + is_blob_field= 1; + } + } last_name= quote_name(row[0], name_buff, 0); if (opt_dump_history && *versioned && opt_update_history && @@ -3520,6 +3540,13 @@ static uint get_table_structure(const char *table, const char *db, char *table_t dynstr_append_checked(&select_field_names, ") as "); dynstr_append_checked(&select_field_names, last_name); } + else if (is_blob_field) + { + dynstr_append_checked(&select_field_names, "HEX("); + dynstr_append_checked(&select_field_names, last_name); + dynstr_append_checked(&select_field_names, ") AS "); + dynstr_append_checked(&select_field_names, last_name); + } else dynstr_append_checked(&select_field_names, last_name); dynstr_append_checked(&insert_field_names, last_name); diff --git a/client/mysqlimport.cc b/client/mysqlimport.cc index 8f92b8b86e1d7..88c4dcce7eb35 100644 --- a/client/mysqlimport.cc +++ b/client/mysqlimport.cc @@ -61,7 +61,7 @@ static std::string parse_sql_script(const char *filepath, bool *tz_utc, static my_bool verbose=0,lock_tables=0,ignore_errors=0,opt_delete=0, replace, silent, ignore, ignore_foreign_keys, - opt_compress, opt_low_priority, tty_password; + opt_compress, opt_low_priority, tty_password, opt_hex_blob; static my_bool debug_info_flag= 0, debug_check_flag= 0; static uint opt_use_threads=0, opt_local_file=0, my_end_arg= 0; static char *opt_password=0, *current_user=0, @@ -259,7 +259,12 @@ static struct my_option my_long_options[] = &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} + {"hex-blob", 0, + "Treat BLOB and BINARY column data as hex-encoded strings. " + "During import, hex strings are converted to binary using UNHEX(). " + "This is useful when importing data exported with HEX() encoding.", + &opt_hex_blob, &opt_hex_blob, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, }; @@ -644,6 +649,57 @@ int table_load_params::create_table_or_view(MYSQL* mysql) return 0; } + +/** + Parse comma-separated column list into vector + + @param columns_str - comma-separated column names (e.g., "col1,col2,col3") + @param result - vector to store parsed column names +*/ +static void parse_column_list(const char* columns_str, + std::vector& result) +{ + if (!columns_str || !*columns_str) + return; + + std::string cols = columns_str; + size_t pos = 0; + + while ((pos = cols.find(',')) != std::string::npos) + { + std::string col = cols.substr(0, pos); + // Trim whitespace + col.erase(0, col.find_first_not_of(" \t\n\r")); + col.erase(col.find_last_not_of(" \t\n\r") + 1); + if (!col.empty()) + result.push_back(col); + cols.erase(0, pos + 1); + } + + // Handle last column + if (!cols.empty()) + { + cols.erase(0, cols.find_first_not_of(" \t\n\r")); + cols.erase(cols.find_last_not_of(" \t\n\r") + 1); + if (!cols.empty()) + result.push_back(cols); + } +} + + +/** + Check if a column type is a BLOB or BINARY type + + @param type - column type string (e.g., "BLOB", "VARBINARY(100)") + @return true if the type is BLOB or BINARY variant +*/ +static bool is_blob_or_binary_type(const std::string& type) +{ + return type.find("blob") != std::string::npos || + type.find("binary") != std::string::npos; +} + + int table_load_params::load_data(MYSQL *mysql) { char tablename[FN_REFLEN], hard_path[FN_REFLEN], @@ -705,7 +761,6 @@ int table_load_params::load_data(MYSQL *mysql) DBUG_RETURN(1); } - bool recreate_secondary_keys= false; if (opt_innodb_optimize_keys && ddl_info.storage_engine == "InnoDB") { @@ -745,15 +800,144 @@ int table_load_params::load_data(MYSQL *mysql) end= strmov(end, " FIELDS"); end= add_load_option(end, fields_terminated, " TERMINATED BY"); end= add_load_option(end, enclosed, " ENCLOSED BY"); - end= add_load_option(end, opt_enclosed, - " OPTIONALLY ENCLOSED BY"); + end= add_load_option(end, opt_enclosed, " OPTIONALLY ENCLOSED BY"); end= add_load_option(end, escaped, " ESCAPED BY"); end= add_load_option(end, lines_terminated, " LINES TERMINATED BY"); + if (opt_ignore_lines >= 0) - end= strmov(longlong10_to_str(opt_ignore_lines, + end= strmov(longlong10_to_str(opt_ignore_lines, strmov(end, " IGNORE "),10), " LINES"); - if (opt_columns) + + // If --hex-blob is enabled but ddl_info.columns is empty (table already exists), + // query the server for table structure + if (opt_hex_blob && ddl_info.columns.empty()) + { + char query_buff[512]; + MYSQL_RES *result; + MYSQL_ROW row; + + // Query INFORMATION_SCHEMA.COLUMNS for table structure + // This is similar to what mysqldump does in get_table_structure() + my_snprintf(query_buff, sizeof(query_buff), + "SELECT column_name, data_type, character_set_name " + "FROM information_schema.columns " + "WHERE table_schema=DATABASE() AND table_name='%s' " + "ORDER BY ordinal_position", tablename); + + if (mysql_query(mysql, query_buff)) + { + db_error_with_table(mysql, tablename); + DBUG_RETURN(1); + } + + result = mysql_store_result(mysql); + if (result) + { + while ((row= mysql_fetch_row(result))) + { + if (row[0] && row[1]) // column_name and data_type + { + std::string col_name = row[0]; + std::string col_type = row[1]; + std::string charset = row[2] ? row[2] : ""; + + // Convert data_type to lowercase for comparison + for (char &c : col_type) c = tolower(c); + + // Store column info + ddl_info.columns.push_back({col_name, col_type}); + } + } + mysql_free_result(result); + + if (verbose && !ddl_info.columns.empty()) + { + fprintf(stdout, "Queried table structure from server: %zu columns found\n", + ddl_info.columns.size()); + } + } + } + + if (opt_hex_blob && !ddl_info.columns.empty()) + { + // Build list of columns to load + std::vector load_columns; + std::vector blob_column_names; // Original column names + + if (opt_columns) + { + // User specified columns - parse them + parse_column_list(opt_columns, load_columns); + } + else + { + // No columns specified - use all columns from table definition + for (const auto& col : ddl_info.columns) { + load_columns.push_back(col.name); + } + } + + // Identify BLOB/BINARY columns and modify their names in load list + for (size_t i = 0; i < load_columns.size(); i++) + { + bool modified = false; + // Check if this column is a BLOB/BINARY type + for (const auto& col : ddl_info.columns) + { + // fprintf(stdout, "col.name %s\n", col.name.c_str()); + if (col.name == load_columns[i] && is_blob_or_binary_type(col.type)) + { + // Replace column name with @var_hex variable + blob_column_names.push_back(col.name); + load_columns[i] = "@" + col.name + "_hex"; + modified = true; + break; + } + } + if (!modified) + load_columns[i] = "`" + load_columns[i] + "`"; + } + + // Build the column specification: (col1, col2, @blob_col_hex, ...) + end= strmov(end, " ("); + for (size_t i = 0; i < load_columns.size(); i++) + { + if (i > 0) + end= strmov(end, ","); + end= strmov(end, load_columns[i].c_str()); + } + end= strmov(end, ")"); + + // Add SET clause to convert hex to binary: SET blob_col=UNHEX(@blob_col_hex) + if (!blob_column_names.empty()) + { + if (verbose) + { + fprintf(stdout, "Converting %zu hex-encoded BLOB/BINARY column(s) to binary\n", + blob_column_names.size()); + } + + end= strmov(end, " SET "); + for (size_t i = 0; i < blob_column_names.size(); i++) + { + if (i > 0) + end= strmov(end, ","); + + // Quote the column name + std::string quoted_col = quote_identifier(blob_column_names[i].c_str()); + end= strmov(end, quoted_col.c_str()); + end= strmov(end, "=UNHEX(@"); + end= strmov(end, blob_column_names[i].c_str()); + end= strmov(end, "_hex)"); + } + } + } + else if (opt_columns) + { + // Original behavior: just use the columns as-is end= strmov(strmov(strmov(end, " ("), opt_columns), ")"); + } + *end= '\0'; if (mysql_query(mysql, sql_statement)) @@ -768,7 +952,6 @@ int table_load_params::load_data(MYSQL *mysql) fprintf(stdout, "%s.%s: %s\n", db, tablename, info); } - if (exec_sql(mysql, std::string("ALTER TABLE ") + full_tablename + " ENABLE KEYS;")) DBUG_RETURN(1); @@ -816,7 +999,6 @@ int table_load_params::load_data(MYSQL *mysql) } - static void lock_table(MYSQL *mysql, int tablecount, char **raw_tablename) { DYNAMIC_STRING query; @@ -1032,19 +1214,19 @@ static char *add_load_option(char *ptr, const char *object, ** "'", "\", "\\" (escaped backslash), "\t" (tab), "\n" (newline) ** This is done by doubling ' and add a end -\ if needed to avoid ** syntax errors from the SQL parser. -*/ +*/ static char *field_escape(char *to,const char *from,uint length) { const char *end; - uint end_backslashes=0; + uint end_backslashes=0; for (end= from+length; from != end; from++) { *to++= *from; if (*from == '\\') end_backslashes^=1; /* find odd number of backslashes */ - else + else { if (*from == '\'' && !end_backslashes) *to++= *from; /* We want a duplicate of "'" for MySQL */ @@ -1053,7 +1235,7 @@ static char *field_escape(char *to,const char *from,uint length) } /* Add missing backslashes if user has specified odd number of backs.*/ if (end_backslashes) - *to++= '\\'; + *to++= '\\'; return to; } diff --git a/mysql-test/main/mariadb-import.result b/mysql-test/main/mariadb-import.result index fb869105bb1be..da265a41354de 100644 --- a/mysql-test/main/mariadb-import.result +++ b/mysql-test/main/mariadb-import.result @@ -387,3 +387,254 @@ use test; mariadb-import: Path 'MYSQLTEST_VARDIR/tmp/non_existing' specified by option '--dir' does not exist # Test too many threads, builtin limit 256 Too many connections, max value for --parallel is 256 +# +# Test --hex-blob option for mariadb-import +# Round-trip tests: export with mysqldump --hex-blob, import with mariadb-import --hex-blob +# +USE test; +# Test 1: Round-trip basic BLOB types with --hex-blob +CREATE TABLE t_blob_roundtrip ( +id INT PRIMARY KEY, +tiny_blob TINYBLOB, +normal_blob BLOB, +medium_blob MEDIUMBLOB, +long_blob LONGBLOB +); +INSERT INTO t_blob_roundtrip VALUES +(1, 'tiny\0data', 'normal\0blob', 'medium\0blob', 'long\0blob'), +(2, NULL, NULL, NULL, NULL), +(3, '', '', '', ''), +(4, X'DEADBEEF', X'CAFEBABE', X'FEEDFACE', X'BAADF00D'), +(5, X'00FF00FF', X'A5A5A5A5', X'12345678', X'FFFFFFFF'); +# Export with mysqldump --hex-blob --tab +# Save original data for comparison +CREATE TABLE t_blob_original LIKE t_blob_roundtrip; +INSERT INTO t_blob_original SELECT * FROM t_blob_roundtrip; +# Drop table and recreate from .sql file +DROP TABLE t_blob_roundtrip; +# Import with mariadb-import --hex-blob +test.t_blob_roundtrip: Records: 5 Deleted: 0 Skipped: 0 Warnings: 0 +# Verify data integrity - should return 0 rows (all data matches) +SELECT * FROM t_blob_roundtrip WHERE id NOT IN (SELECT id FROM t_blob_original); +id tiny_blob normal_blob medium_blob long_blob +SELECT * FROM t_blob_original WHERE id NOT IN (SELECT id FROM t_blob_roundtrip); +id tiny_blob normal_blob medium_blob long_blob +SELECT o.id FROM t_blob_original o, t_blob_roundtrip r +WHERE o.id = r.id +AND (o.tiny_blob IS NOT NULL AND r.tiny_blob IS NOT NULL AND o.tiny_blob != r.tiny_blob); +id +# Show successful import +SELECT id, HEX(tiny_blob), HEX(normal_blob) FROM t_blob_roundtrip ORDER BY id; +id HEX(tiny_blob) HEX(normal_blob) +1 74696E790064617461 6E6F726D616C00626C6F62 +2 NULL NULL +3 +4 DEADBEEF CAFEBABE +5 00FF00FF A5A5A5A5 +DROP TABLE t_blob_roundtrip; +DROP TABLE t_blob_original; +# Test 2: Round-trip BINARY and VARBINARY types +CREATE TABLE t_binary_roundtrip ( +id INT PRIMARY KEY, +bin_col BINARY(10), +varbin_col VARBINARY(100) +); +INSERT INTO t_binary_roundtrip VALUES +(1, X'0102030405', 'varbinary\0test'), +(2, NULL, NULL), +(3, X'00', ''), +(4, X'FFFFFFFFFFFFFF', X'DEADBEEFCAFEBABE'); +# Export with mysqldump --hex-blob --tab +CREATE TABLE t_binary_original LIKE t_binary_roundtrip; +INSERT INTO t_binary_original SELECT * FROM t_binary_roundtrip; +DROP TABLE t_binary_roundtrip; +# Import with mariadb-import --hex-blob +test.t_binary_roundtrip: Records: 4 Deleted: 0 Skipped: 0 Warnings: 0 +# Verify data integrity +SELECT o.id FROM t_binary_original o, t_binary_roundtrip r +WHERE o.id = r.id +AND ((o.bin_col IS NOT NULL AND r.bin_col IS NOT NULL AND o.bin_col != r.bin_col) +OR (o.varbin_col IS NOT NULL AND r.varbin_col IS NOT NULL AND o.varbin_col != r.varbin_col)); +id +# Show successful import +SELECT id, HEX(bin_col), HEX(varbin_col) FROM t_binary_roundtrip ORDER BY id; +id HEX(bin_col) HEX(varbin_col) +1 01020304050000000000 76617262696E6172790074657374 +2 NULL NULL +3 00000000000000000000 +4 FFFFFFFFFFFFFF000000 DEADBEEFCAFEBABE +DROP TABLE t_binary_roundtrip; +DROP TABLE t_binary_original; +# Test 3: Round-trip mixed table (BLOB and non-BLOB columns) +CREATE TABLE t_mixed_roundtrip ( +id INT PRIMARY KEY, +name VARCHAR(50), +data BLOB, +description TEXT, +binary_data VARBINARY(100) +); +INSERT INTO t_mixed_roundtrip VALUES +(1, 'First', X'ABCD', 'Description 1', X'1234'), +(2, 'Second', NULL, 'Description 2', NULL), +(3, 'Third', '', 'Description 3', ''), +(4, 'Special\tChars', X'00FF00FF', 'Desc\nWith\nNewlines', X'CAFEBABE'); +# Export with mysqldump --hex-blob --tab +CREATE TABLE t_mixed_original LIKE t_mixed_roundtrip; +INSERT INTO t_mixed_original SELECT * FROM t_mixed_roundtrip; +DROP TABLE t_mixed_roundtrip; +# Import with mariadb-import --hex-blob +test.t_mixed_roundtrip: Records: 4 Deleted: 0 Skipped: 0 Warnings: 0 +# Verify all columns match +SELECT o.id FROM t_mixed_original o, t_mixed_roundtrip r +WHERE o.id = r.id +AND (o.name != r.name OR o.description != r.description +OR (o.data IS NOT NULL AND r.data IS NOT NULL AND o.data != r.data) +OR (o.binary_data IS NOT NULL AND r.binary_data IS NOT NULL AND o.binary_data != r.binary_data)); +id +# Show successful import +SELECT id, name, HEX(data), description, HEX(binary_data) FROM t_mixed_roundtrip ORDER BY id; +id name HEX(data) description HEX(binary_data) +1 First ABCD Description 1 1234 +2 Second NULL Description 2 NULL +3 Third Description 3 +4 Special Chars 00FF00FF Desc +With +Newlines CAFEBABE +DROP TABLE t_mixed_roundtrip; +DROP TABLE t_mixed_original; +# Test 4: Round-trip with --columns parameter (subset of columns) +CREATE TABLE t_columns_test ( +id INT PRIMARY KEY, +name VARCHAR(50), +blob1 BLOB, +blob2 BLOB, +text_col TEXT +); +INSERT INTO t_columns_test VALUES +(1, 'Row1', X'ABCD', X'1234', 'Text1'), +(2, 'Row2', NULL, NULL, 'Text2'), +(3, 'Row3', X'BEEF', X'CAFE', 'Text3'); +# Export with mysqldump --hex-blob --tab +CREATE TABLE t_columns_original LIKE t_columns_test; +INSERT INTO t_columns_original SELECT * FROM t_columns_test; +DROP TABLE t_columns_test; +# Import with --hex-blob and --columns (id, blob1, blob2) +test.t_columns_test: Records: 3 Deleted: 0 Skipped: 0 Warnings: 0 +# Verify blob columns match +SELECT o.id FROM t_columns_original o, t_columns_test r +WHERE o.id = r.id +AND ((o.blob1 IS NOT NULL AND r.blob1 IS NOT NULL AND o.blob1 != r.blob1) +OR (o.blob2 IS NOT NULL AND r.blob2 IS NOT NULL AND o.blob2 != r.blob2)); +id +# Show successful import +SELECT id, name, HEX(blob1), HEX(blob2), text_col FROM t_columns_test ORDER BY id; +id name HEX(blob1) HEX(blob2) text_col +1 Row1 ABCD 1234 Text1 +2 Row2 NULL NULL Text2 +3 Row3 BEEF CAFE Text3 +DROP TABLE t_columns_test; +DROP TABLE t_columns_original; +# Test 5: Import WITHOUT --hex-blob (default behavior - should fail or produce wrong data) +CREATE TABLE t_no_hex_import ( +id INT, +data BLOB +); +INSERT INTO t_no_hex_import VALUES (1, X'DEADBEEF'), (2, NULL); +CREATE TABLE t_no_hex_original LIKE t_no_hex_import; +INSERT INTO t_no_hex_original SELECT * FROM t_no_hex_import; +DROP TABLE t_no_hex_import; +# Import WITHOUT --hex-blob - data will be interpreted as ASCII string, not binary +test.t_no_hex_import: Records: 2 Deleted: 0 Skipped: 0 Warnings: 0 +# Show data mismatch - imported data is ASCII "DEADBEEF" not binary 0xDEADBEEF +SELECT id, HEX(data) as hex_data, LENGTH(data) as data_length FROM t_no_hex_import ORDER BY id; +id hex_data data_length +1 4445414442454546 8 +2 NULL NULL +# Original was binary (4 bytes), imported is ASCII string (8 bytes) +SELECT id, HEX(data) as hex_data, LENGTH(data) as data_length FROM t_no_hex_original ORDER BY id; +id hex_data data_length +1 DEADBEEF 4 +2 NULL NULL +DROP TABLE t_no_hex_import; +DROP TABLE t_no_hex_original; +# Test 6: NULL and empty blob handling +CREATE TABLE t_null_empty ( +id INT PRIMARY KEY, +blob_null BLOB, +blob_empty BLOB, +blob_data BLOB +); +INSERT INTO t_null_empty VALUES +(1, NULL, '', X'ABCD'), +(2, NULL, '', X'1234'); +CREATE TABLE t_null_empty_original LIKE t_null_empty; +INSERT INTO t_null_empty_original SELECT * FROM t_null_empty; +DROP TABLE t_null_empty; +test.t_null_empty: Records: 2 Deleted: 0 Skipped: 0 Warnings: 0 +# Verify NULL remains NULL and empty remains empty +SELECT id, blob_null IS NULL as is_null, LENGTH(blob_empty) as empty_length, HEX(blob_data) FROM t_null_empty ORDER BY id; +id is_null empty_length HEX(blob_data) +1 1 0 ABCD +2 1 0 1234 +SELECT o.id FROM t_null_empty_original o, t_null_empty r +WHERE o.id = r.id +AND ((o.blob_null IS NULL) != (r.blob_null IS NULL) +OR o.blob_empty != r.blob_empty +OR o.blob_data != r.blob_data); +id +DROP TABLE t_null_empty; +DROP TABLE t_null_empty_original; +# Test 7: Table with only BLOB columns +CREATE TABLE t_all_blobs ( +blob1 BLOB, +blob2 TINYBLOB, +blob3 MEDIUMBLOB +); +INSERT INTO t_all_blobs VALUES +(X'ABCDEF', X'123456', X'FEDCBA'), +(NULL, NULL, NULL), +(X'FF', X'00', X'AA'); +CREATE TABLE t_all_blobs_original LIKE t_all_blobs; +INSERT INTO t_all_blobs_original SELECT * FROM t_all_blobs; +DROP TABLE t_all_blobs; +test.t_all_blobs: Records: 3 Deleted: 0 Skipped: 0 Warnings: 0 +# Verify all blob columns match +SELECT HEX(blob1), HEX(blob2), HEX(blob3) FROM t_all_blobs ORDER BY blob1; +HEX(blob1) HEX(blob2) HEX(blob3) +NULL NULL NULL +ABCDEF 123456 FEDCBA +FF 00 AA +SELECT o.blob1 FROM t_all_blobs_original o +WHERE NOT EXISTS ( +SELECT * FROM t_all_blobs r +WHERE (o.blob1 IS NULL AND r.blob1 IS NULL) OR o.blob1 = r.blob1 +); +blob1 +DROP TABLE t_all_blobs; +DROP TABLE t_all_blobs_original; +# Test 8: Round-trip with --dir option +CREATE TABLE t_dir_import ( +id INT PRIMARY KEY, +data BLOB +); +INSERT INTO t_dir_import VALUES +(1, X'DEADBEEF'), +(2, 'test\0data'), +(3, NULL); +CREATE TABLE t_dir_import_original LIKE t_dir_import; +INSERT INTO t_dir_import_original SELECT * FROM t_dir_import; +DROP TABLE t_dir_import; +# Import with mariadb-import --hex-blob from --dir export +test.t_dir_import: Records: 3 Deleted: 0 Skipped: 0 Warnings: 0 +# Verify data integrity +SELECT o.id FROM t_dir_import_original o, t_dir_import r +WHERE o.id = r.id +AND ((o.data IS NOT NULL AND r.data IS NOT NULL AND o.data != r.data)); +id +SELECT id, HEX(data) FROM t_dir_import ORDER BY id; +id HEX(data) +1 DEADBEEF +2 746573740064617461 +3 NULL +DROP TABLE t_dir_import; +DROP TABLE t_dir_import_original; diff --git a/mysql-test/main/mariadb-import.test b/mysql-test/main/mariadb-import.test index 29d7ed7f2f379..1d5798ed03645 100644 --- a/mysql-test/main/mariadb-import.test +++ b/mysql-test/main/mariadb-import.test @@ -235,3 +235,312 @@ use test; --exec $MYSQL_IMPORT --dir $MYSQLTEST_VARDIR/tmp/dump --parallel=300 2>&1 --rmdir $MYSQLTEST_VARDIR/tmp/dump + +--echo # +--echo # Test --hex-blob option for mariadb-import +--echo # Round-trip tests: export with mysqldump --hex-blob, import with mariadb-import --hex-blob +--echo # + +USE test; + +--echo # Test 1: Round-trip basic BLOB types with --hex-blob +CREATE TABLE t_blob_roundtrip ( + id INT PRIMARY KEY, + tiny_blob TINYBLOB, + normal_blob BLOB, + medium_blob MEDIUMBLOB, + long_blob LONGBLOB +); + +INSERT INTO t_blob_roundtrip VALUES + (1, 'tiny\0data', 'normal\0blob', 'medium\0blob', 'long\0blob'), + (2, NULL, NULL, NULL, NULL), + (3, '', '', '', ''), + (4, X'DEADBEEF', X'CAFEBABE', X'FEEDFACE', X'BAADF00D'), + (5, X'00FF00FF', X'A5A5A5A5', X'12345678', X'FFFFFFFF'); + +--echo # Export with mysqldump --hex-blob --tab +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_blob_roundtrip + +--echo # Save original data for comparison +CREATE TABLE t_blob_original LIKE t_blob_roundtrip; +INSERT INTO t_blob_original SELECT * FROM t_blob_roundtrip; + +--echo # Drop table and recreate from .sql file +DROP TABLE t_blob_roundtrip; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t_blob_roundtrip.sql + +--echo # Import with mariadb-import --hex-blob +--exec $MYSQL_IMPORT --default-character-set=utf8mb4 --hex-blob test $MYSQLTEST_VARDIR/tmp/t_blob_roundtrip.txt + +--echo # Verify data integrity - should return 0 rows (all data matches) +SELECT * FROM t_blob_roundtrip WHERE id NOT IN (SELECT id FROM t_blob_original); +SELECT * FROM t_blob_original WHERE id NOT IN (SELECT id FROM t_blob_roundtrip); +SELECT o.id FROM t_blob_original o, t_blob_roundtrip r +WHERE o.id = r.id +AND (o.tiny_blob IS NOT NULL AND r.tiny_blob IS NOT NULL AND o.tiny_blob != r.tiny_blob); + +--echo # Show successful import +SELECT id, HEX(tiny_blob), HEX(normal_blob) FROM t_blob_roundtrip ORDER BY id; + +--remove_file $MYSQLTEST_VARDIR/tmp/t_blob_roundtrip.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_blob_roundtrip.txt +DROP TABLE t_blob_roundtrip; +DROP TABLE t_blob_original; + +--echo # Test 2: Round-trip BINARY and VARBINARY types +CREATE TABLE t_binary_roundtrip ( + id INT PRIMARY KEY, + bin_col BINARY(10), + varbin_col VARBINARY(100) +); + +INSERT INTO t_binary_roundtrip VALUES + (1, X'0102030405', 'varbinary\0test'), + (2, NULL, NULL), + (3, X'00', ''), + (4, X'FFFFFFFFFFFFFF', X'DEADBEEFCAFEBABE'); + +--echo # Export with mysqldump --hex-blob --tab +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_binary_roundtrip + +CREATE TABLE t_binary_original LIKE t_binary_roundtrip; +INSERT INTO t_binary_original SELECT * FROM t_binary_roundtrip; + +DROP TABLE t_binary_roundtrip; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t_binary_roundtrip.sql + +--echo # Import with mariadb-import --hex-blob +--exec $MYSQL_IMPORT --default-character-set=utf8mb4 --hex-blob test $MYSQLTEST_VARDIR/tmp/t_binary_roundtrip.txt + +--echo # Verify data integrity +SELECT o.id FROM t_binary_original o, t_binary_roundtrip r +WHERE o.id = r.id +AND ((o.bin_col IS NOT NULL AND r.bin_col IS NOT NULL AND o.bin_col != r.bin_col) + OR (o.varbin_col IS NOT NULL AND r.varbin_col IS NOT NULL AND o.varbin_col != r.varbin_col)); + +--echo # Show successful import +SELECT id, HEX(bin_col), HEX(varbin_col) FROM t_binary_roundtrip ORDER BY id; + +--remove_file $MYSQLTEST_VARDIR/tmp/t_binary_roundtrip.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_binary_roundtrip.txt +DROP TABLE t_binary_roundtrip; +DROP TABLE t_binary_original; + +--echo # Test 3: Round-trip mixed table (BLOB and non-BLOB columns) +CREATE TABLE t_mixed_roundtrip ( + id INT PRIMARY KEY, + name VARCHAR(50), + data BLOB, + description TEXT, + binary_data VARBINARY(100) +); + +INSERT INTO t_mixed_roundtrip VALUES + (1, 'First', X'ABCD', 'Description 1', X'1234'), + (2, 'Second', NULL, 'Description 2', NULL), + (3, 'Third', '', 'Description 3', ''), + (4, 'Special\tChars', X'00FF00FF', 'Desc\nWith\nNewlines', X'CAFEBABE'); + +--echo # Export with mysqldump --hex-blob --tab +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_mixed_roundtrip + +CREATE TABLE t_mixed_original LIKE t_mixed_roundtrip; +INSERT INTO t_mixed_original SELECT * FROM t_mixed_roundtrip; + +DROP TABLE t_mixed_roundtrip; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t_mixed_roundtrip.sql + +--echo # Import with mariadb-import --hex-blob +--exec $MYSQL_IMPORT --default-character-set=utf8mb4 --hex-blob test $MYSQLTEST_VARDIR/tmp/t_mixed_roundtrip.txt + +--echo # Verify all columns match +SELECT o.id FROM t_mixed_original o, t_mixed_roundtrip r +WHERE o.id = r.id +AND (o.name != r.name OR o.description != r.description + OR (o.data IS NOT NULL AND r.data IS NOT NULL AND o.data != r.data) + OR (o.binary_data IS NOT NULL AND r.binary_data IS NOT NULL AND o.binary_data != r.binary_data)); + +--echo # Show successful import +SELECT id, name, HEX(data), description, HEX(binary_data) FROM t_mixed_roundtrip ORDER BY id; + +--remove_file $MYSQLTEST_VARDIR/tmp/t_mixed_roundtrip.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_mixed_roundtrip.txt +DROP TABLE t_mixed_roundtrip; +DROP TABLE t_mixed_original; + +--echo # Test 4: Round-trip with --columns parameter (subset of columns) +CREATE TABLE t_columns_test ( + id INT PRIMARY KEY, + name VARCHAR(50), + blob1 BLOB, + blob2 BLOB, + text_col TEXT +); + +INSERT INTO t_columns_test VALUES + (1, 'Row1', X'ABCD', X'1234', 'Text1'), + (2, 'Row2', NULL, NULL, 'Text2'), + (3, 'Row3', X'BEEF', X'CAFE', 'Text3'); + +--echo # Export with mysqldump --hex-blob --tab +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_columns_test + +CREATE TABLE t_columns_original LIKE t_columns_test; +INSERT INTO t_columns_original SELECT * FROM t_columns_test; + +DROP TABLE t_columns_test; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t_columns_test.sql + +--echo # Import with --hex-blob and --columns (id, blob1, blob2) +--exec $MYSQL_IMPORT --default-character-set=utf8mb4 --hex-blob --columns=id,name,blob1,blob2,text_col test $MYSQLTEST_VARDIR/tmp/t_columns_test.txt + +--echo # Verify blob columns match +SELECT o.id FROM t_columns_original o, t_columns_test r +WHERE o.id = r.id +AND ((o.blob1 IS NOT NULL AND r.blob1 IS NOT NULL AND o.blob1 != r.blob1) + OR (o.blob2 IS NOT NULL AND r.blob2 IS NOT NULL AND o.blob2 != r.blob2)); + +--echo # Show successful import +SELECT id, name, HEX(blob1), HEX(blob2), text_col FROM t_columns_test ORDER BY id; + +--remove_file $MYSQLTEST_VARDIR/tmp/t_columns_test.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_columns_test.txt +DROP TABLE t_columns_test; +DROP TABLE t_columns_original; + +--echo # Test 5: Import WITHOUT --hex-blob (default behavior - should fail or produce wrong data) +CREATE TABLE t_no_hex_import ( + id INT, + data BLOB +); + +INSERT INTO t_no_hex_import VALUES (1, X'DEADBEEF'), (2, NULL); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_no_hex_import + +CREATE TABLE t_no_hex_original LIKE t_no_hex_import; +INSERT INTO t_no_hex_original SELECT * FROM t_no_hex_import; + +DROP TABLE t_no_hex_import; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t_no_hex_import.sql + +--echo # Import WITHOUT --hex-blob - data will be interpreted as ASCII string, not binary +--exec $MYSQL_IMPORT --default-character-set=utf8mb4 test $MYSQLTEST_VARDIR/tmp/t_no_hex_import.txt + +--echo # Show data mismatch - imported data is ASCII "DEADBEEF" not binary 0xDEADBEEF +SELECT id, HEX(data) as hex_data, LENGTH(data) as data_length FROM t_no_hex_import ORDER BY id; +--echo # Original was binary (4 bytes), imported is ASCII string (8 bytes) +SELECT id, HEX(data) as hex_data, LENGTH(data) as data_length FROM t_no_hex_original ORDER BY id; + +--remove_file $MYSQLTEST_VARDIR/tmp/t_no_hex_import.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_no_hex_import.txt +DROP TABLE t_no_hex_import; +DROP TABLE t_no_hex_original; + +--echo # Test 6: NULL and empty blob handling +CREATE TABLE t_null_empty ( + id INT PRIMARY KEY, + blob_null BLOB, + blob_empty BLOB, + blob_data BLOB +); + +INSERT INTO t_null_empty VALUES + (1, NULL, '', X'ABCD'), + (2, NULL, '', X'1234'); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_null_empty + +CREATE TABLE t_null_empty_original LIKE t_null_empty; +INSERT INTO t_null_empty_original SELECT * FROM t_null_empty; + +DROP TABLE t_null_empty; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t_null_empty.sql + +--exec $MYSQL_IMPORT --default-character-set=utf8mb4 --hex-blob test $MYSQLTEST_VARDIR/tmp/t_null_empty.txt + +--echo # Verify NULL remains NULL and empty remains empty +SELECT id, blob_null IS NULL as is_null, LENGTH(blob_empty) as empty_length, HEX(blob_data) FROM t_null_empty ORDER BY id; +SELECT o.id FROM t_null_empty_original o, t_null_empty r +WHERE o.id = r.id +AND ((o.blob_null IS NULL) != (r.blob_null IS NULL) + OR o.blob_empty != r.blob_empty + OR o.blob_data != r.blob_data); + +--remove_file $MYSQLTEST_VARDIR/tmp/t_null_empty.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_null_empty.txt +DROP TABLE t_null_empty; +DROP TABLE t_null_empty_original; + +--echo # Test 7: Table with only BLOB columns +CREATE TABLE t_all_blobs ( + blob1 BLOB, + blob2 TINYBLOB, + blob3 MEDIUMBLOB +); + +INSERT INTO t_all_blobs VALUES + (X'ABCDEF', X'123456', X'FEDCBA'), + (NULL, NULL, NULL), + (X'FF', X'00', X'AA'); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_all_blobs + +CREATE TABLE t_all_blobs_original LIKE t_all_blobs; +INSERT INTO t_all_blobs_original SELECT * FROM t_all_blobs; + +DROP TABLE t_all_blobs; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t_all_blobs.sql + +--exec $MYSQL_IMPORT --default-character-set=utf8mb4 --hex-blob test $MYSQLTEST_VARDIR/tmp/t_all_blobs.txt + +--echo # Verify all blob columns match +SELECT HEX(blob1), HEX(blob2), HEX(blob3) FROM t_all_blobs ORDER BY blob1; +SELECT o.blob1 FROM t_all_blobs_original o +WHERE NOT EXISTS ( + SELECT * FROM t_all_blobs r + WHERE (o.blob1 IS NULL AND r.blob1 IS NULL) OR o.blob1 = r.blob1 +); + +--remove_file $MYSQLTEST_VARDIR/tmp/t_all_blobs.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_all_blobs.txt +DROP TABLE t_all_blobs; +DROP TABLE t_all_blobs_original; + +--echo # Test 8: Round-trip with --dir option +CREATE TABLE t_dir_import ( + id INT PRIMARY KEY, + data BLOB +); + +INSERT INTO t_dir_import VALUES + (1, X'DEADBEEF'), + (2, 'test\0data'), + (3, NULL); + +--mkdir $MYSQLTEST_VARDIR/tmp/dump_hex +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --dir=$MYSQLTEST_VARDIR/tmp/dump_hex test + +CREATE TABLE t_dir_import_original LIKE t_dir_import; +INSERT INTO t_dir_import_original SELECT * FROM t_dir_import; + +DROP TABLE t_dir_import; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/dump_hex/test/t_dir_import.sql + +--echo # Import with mariadb-import --hex-blob from --dir export +--exec $MYSQL_IMPORT --default-character-set=utf8mb4 --hex-blob test $MYSQLTEST_VARDIR/tmp/dump_hex/test/t_dir_import.txt + +--echo # Verify data integrity +SELECT o.id FROM t_dir_import_original o, t_dir_import r +WHERE o.id = r.id +AND ((o.data IS NOT NULL AND r.data IS NOT NULL AND o.data != r.data)); + +SELECT id, HEX(data) FROM t_dir_import ORDER BY id; + +--remove_file $MYSQLTEST_VARDIR/tmp/dump_hex/test/t_dir_import.sql +--remove_file $MYSQLTEST_VARDIR/tmp/dump_hex/test/t_dir_import.txt +--rmdir $MYSQLTEST_VARDIR/tmp/dump_hex/test +--rmdir $MYSQLTEST_VARDIR/tmp/dump_hex +DROP TABLE t_dir_import; +DROP TABLE t_dir_import_original; diff --git a/mysql-test/main/mysqldump-hexblob.result b/mysql-test/main/mysqldump-hexblob.result new file mode 100644 index 0000000000000..12b2fee5e9499 --- /dev/null +++ b/mysql-test/main/mysqldump-hexblob.result @@ -0,0 +1,411 @@ +# +# Test --hex-blob with --tab (--dir) +# Verify that BLOB and BINARY columns are hex-encoded in tab-separated output +# +USE test; +# Test 1: Basic BLOB types with hex-blob +CREATE TABLE t_hex_blob ( +id INT PRIMARY KEY, +tiny_blob TINYBLOB, +normal_blob BLOB, +medium_blob MEDIUMBLOB, +long_blob LONGBLOB +); +INSERT INTO t_hex_blob VALUES +(1, 'tiny\0data', 'normal\0blob', 'medium\0blob', 'long\0blob'), +(2, NULL, NULL, NULL, NULL), +(3, '', '', '', ''), +(4, X'DEADBEEF', X'CAFEBABE', X'FEEDFACE', X'BAADF00D'); +# Check SQL file +/*M!999999\- enable the sandbox mode */ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; +DROP TABLE IF EXISTS `t_hex_blob`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `t_hex_blob` ( + `id` int(11) NOT NULL, + `tiny_blob` tinyblob DEFAULT NULL, + `normal_blob` blob DEFAULT NULL, + `medium_blob` mediumblob DEFAULT NULL, + `long_blob` longblob DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; + +# Check data file - blob data should be hex-encoded +1 74696E790064617461 6E6F726D616C00626C6F62 6D656469756D00626C6F62 6C6F6E6700626C6F62 +2 \N \N \N \N +3 +4 DEADBEEF CAFEBABE FEEDFACE BAADF00D +DROP TABLE t_hex_blob; +# Test 2: BINARY and VARBINARY types with hex-blob +CREATE TABLE t_hex_binary ( +id INT PRIMARY KEY, +bin_col BINARY(10), +varbin_col VARBINARY(100) +); +INSERT INTO t_hex_binary VALUES +(1, X'0102030405', 'varbinary\0test'), +(2, NULL, NULL), +(3, X'00', ''), +(4, X'FFFFFFFFFFFFFF', X'DEADBEEFCAFEBABE'); +# Check SQL file +/*M!999999\- enable the sandbox mode */ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; +DROP TABLE IF EXISTS `t_hex_binary`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `t_hex_binary` ( + `id` int(11) NOT NULL, + `bin_col` binary(10) DEFAULT NULL, + `varbin_col` varbinary(100) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; + +# Check data file - binary data should be hex-encoded +1 01020304050000000000 76617262696E6172790074657374 +2 \N \N +3 00000000000000000000 +4 FFFFFFFFFFFFFF000000 DEADBEEFCAFEBABE +DROP TABLE t_hex_binary; +# Test 3: Mixed table with BLOB and non-BLOB columns +CREATE TABLE t_hex_mixed ( +id INT PRIMARY KEY, +name VARCHAR(50), +data BLOB, +description TEXT, +binary_data VARBINARY(100) +); +INSERT INTO t_hex_mixed VALUES +(1, 'First', X'ABCD', 'Description 1', X'1234'), +(2, 'Second', NULL, 'Description 2', NULL), +(3, 'Third', '', 'Description 3', ''), +(4, 'Special\tChars', X'00FF00FF', 'Desc\nWith\nNewlines', X'CAFEBABE'); +# Check SQL file - should have HEX() wrapper for BLOB/BINARY columns +/*M!999999\- enable the sandbox mode */ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; +DROP TABLE IF EXISTS `t_hex_mixed`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `t_hex_mixed` ( + `id` int(11) NOT NULL, + `name` varchar(50) DEFAULT NULL, + `data` blob DEFAULT NULL, + `description` text DEFAULT NULL, + `binary_data` varbinary(100) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; + +# Check data file - only BLOB/BINARY columns should be hex-encoded +1 First ABCD Description 1 1234 +2 Second \N Description 2 \N +3 Third Description 3 +4 Special\ Chars 00FF00FF Desc\ +With\ +Newlines CAFEBABE +DROP TABLE t_hex_mixed; +# Test 4: Verify --tab without --hex-blob (default behavior) +CREATE TABLE t_no_hex ( +id INT, +blob_col BLOB +); +INSERT INTO t_no_hex VALUES (1, 'test\0data'), (2, NULL); +# Check SQL file - should NOT have HEX() wrapper +/*M!999999\- enable the sandbox mode */ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; +DROP TABLE IF EXISTS `t_no_hex`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `t_no_hex` ( + `id` int(11) DEFAULT NULL, + `blob_col` blob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; + +# Check data file - blob data NOT hex-encoded (binary content) +1 test\0data +2 \N +DROP TABLE t_no_hex; +# Test 5: Table with only BLOB columns +CREATE TABLE t_all_blobs ( +blob1 BLOB, +blob2 TINYBLOB, +blob3 MEDIUMBLOB +); +INSERT INTO t_all_blobs VALUES +(X'ABCDEF', X'123456', X'FEDCBA'), +(NULL, NULL, NULL); +# Check SQL file +/*M!999999\- enable the sandbox mode */ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; +DROP TABLE IF EXISTS `t_all_blobs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `t_all_blobs` ( + `blob1` blob DEFAULT NULL, + `blob2` tinyblob DEFAULT NULL, + `blob3` mediumblob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; + +# Check data file +ABCDEF 123456 FEDCBA +\N \N \N +DROP TABLE t_all_blobs; +# Test 6: Empty table with BLOB columns +CREATE TABLE t_empty_blobs ( +id INT, +data BLOB +); +# Check SQL file +/*M!999999\- enable the sandbox mode */ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; +DROP TABLE IF EXISTS `t_empty_blobs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `t_empty_blobs` ( + `id` int(11) DEFAULT NULL, + `data` blob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; + +# Check data file - should be empty +DROP TABLE t_empty_blobs; +# +# Test --hex-blob with --dir option +# --dir creates subdirectories for each database +# +# Test 7: Basic BLOB types with --hex-blob and --dir +CREATE TABLE t_dir_hex_blob ( +id INT PRIMARY KEY, +tiny_blob TINYBLOB, +normal_blob BLOB, +medium_blob MEDIUMBLOB, +long_blob LONGBLOB +); +INSERT INTO t_dir_hex_blob VALUES +(1, 'tiny\0data', 'normal\0blob', 'medium\0blob', 'long\0blob'), +(2, NULL, NULL, NULL, NULL), +(3, '', '', '', ''), +(4, X'DEADBEEF', X'CAFEBABE', X'FEEDFACE', X'BAADF00D'); +# Check SQL file in test subdirectory +/*M!999999\- enable the sandbox mode */ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; +DROP TABLE IF EXISTS `t_dir_hex_blob`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `t_dir_hex_blob` ( + `id` int(11) NOT NULL, + `tiny_blob` tinyblob DEFAULT NULL, + `normal_blob` blob DEFAULT NULL, + `medium_blob` mediumblob DEFAULT NULL, + `long_blob` longblob DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; + +# Check data file - blob data should be hex-encoded +1 74696E790064617461 6E6F726D616C00626C6F62 6D656469756D00626C6F62 6C6F6E6700626C6F62 +2 \N \N \N \N +3 +4 DEADBEEF CAFEBABE FEEDFACE BAADF00D +DROP TABLE t_dir_hex_blob; +# Test 8: Mixed table with --dir and --hex-blob +CREATE TABLE t_dir_mixed ( +id INT PRIMARY KEY, +name VARCHAR(50), +data BLOB, +binary_data VARBINARY(50) +); +INSERT INTO t_dir_mixed VALUES +(1, 'First', X'ABCD', X'1234'), +(2, 'Second', NULL, NULL), +(3, 'Third', '', ''); +# Check SQL file +/*M!999999\- enable the sandbox mode */ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; +DROP TABLE IF EXISTS `t_dir_mixed`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `t_dir_mixed` ( + `id` int(11) NOT NULL, + `name` varchar(50) DEFAULT NULL, + `data` blob DEFAULT NULL, + `binary_data` varbinary(50) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; + +# Check data file - only BLOB/BINARY columns should be hex-encoded +1 First ABCD 1234 +2 Second \N \N +3 Third +DROP TABLE t_dir_mixed; +# Test 9: Verify --dir without --hex-blob (default behavior) +CREATE TABLE t_dir_no_hex ( +id INT, +blob_col BLOB +); +INSERT INTO t_dir_no_hex VALUES (1, 'test\0data'), (2, NULL); +# Check SQL file - should NOT have HEX() wrapper +/*M!999999\- enable the sandbox mode */ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; +DROP TABLE IF EXISTS `t_dir_no_hex`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `t_dir_no_hex` ( + `id` int(11) DEFAULT NULL, + `blob_col` blob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; + +# Check data file - blob data NOT hex-encoded +1 test\0data +2 \N +DROP TABLE t_dir_no_hex; diff --git a/mysql-test/main/mysqldump-hexblob.test b/mysql-test/main/mysqldump-hexblob.test new file mode 100644 index 0000000000000..3c370904f2e2e --- /dev/null +++ b/mysql-test/main/mysqldump-hexblob.test @@ -0,0 +1,213 @@ +# Test --hex-blob with --tab (--dir) +# Verify that BLOB and BINARY columns are hex-encoded in tab-separated output + +--source include/not_embedded.inc + +--echo # +--echo # Test --hex-blob with --tab (--dir) +--echo # Verify that BLOB and BINARY columns are hex-encoded in tab-separated output +--echo # + +USE test; + +--echo # Test 1: Basic BLOB types with hex-blob +CREATE TABLE t_hex_blob ( + id INT PRIMARY KEY, + tiny_blob TINYBLOB, + normal_blob BLOB, + medium_blob MEDIUMBLOB, + long_blob LONGBLOB +); + +INSERT INTO t_hex_blob VALUES + (1, 'tiny\0data', 'normal\0blob', 'medium\0blob', 'long\0blob'), + (2, NULL, NULL, NULL, NULL), + (3, '', '', '', ''), + (4, X'DEADBEEF', X'CAFEBABE', X'FEEDFACE', X'BAADF00D'); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_hex_blob +--echo # Check SQL file +--cat_file $MYSQLTEST_VARDIR/tmp/t_hex_blob.sql +--echo # Check data file - blob data should be hex-encoded +--cat_file $MYSQLTEST_VARDIR/tmp/t_hex_blob.txt +--remove_file $MYSQLTEST_VARDIR/tmp/t_hex_blob.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_hex_blob.txt + +DROP TABLE t_hex_blob; + +--echo # Test 2: BINARY and VARBINARY types with hex-blob +CREATE TABLE t_hex_binary ( + id INT PRIMARY KEY, + bin_col BINARY(10), + varbin_col VARBINARY(100) +); + +INSERT INTO t_hex_binary VALUES + (1, X'0102030405', 'varbinary\0test'), + (2, NULL, NULL), + (3, X'00', ''), + (4, X'FFFFFFFFFFFFFF', X'DEADBEEFCAFEBABE'); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_hex_binary +--echo # Check SQL file +--cat_file $MYSQLTEST_VARDIR/tmp/t_hex_binary.sql +--echo # Check data file - binary data should be hex-encoded +--cat_file $MYSQLTEST_VARDIR/tmp/t_hex_binary.txt +--remove_file $MYSQLTEST_VARDIR/tmp/t_hex_binary.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_hex_binary.txt + +DROP TABLE t_hex_binary; + +--echo # Test 3: Mixed table with BLOB and non-BLOB columns +CREATE TABLE t_hex_mixed ( + id INT PRIMARY KEY, + name VARCHAR(50), + data BLOB, + description TEXT, + binary_data VARBINARY(100) +); + +INSERT INTO t_hex_mixed VALUES + (1, 'First', X'ABCD', 'Description 1', X'1234'), + (2, 'Second', NULL, 'Description 2', NULL), + (3, 'Third', '', 'Description 3', ''), + (4, 'Special\tChars', X'00FF00FF', 'Desc\nWith\nNewlines', X'CAFEBABE'); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_hex_mixed +--echo # Check SQL file - should have HEX() wrapper for BLOB/BINARY columns +--cat_file $MYSQLTEST_VARDIR/tmp/t_hex_mixed.sql +--echo # Check data file - only BLOB/BINARY columns should be hex-encoded +--cat_file $MYSQLTEST_VARDIR/tmp/t_hex_mixed.txt +--remove_file $MYSQLTEST_VARDIR/tmp/t_hex_mixed.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_hex_mixed.txt + +DROP TABLE t_hex_mixed; + +--echo # Test 4: Verify --tab without --hex-blob (default behavior) +CREATE TABLE t_no_hex ( + id INT, + blob_col BLOB +); + +INSERT INTO t_no_hex VALUES (1, 'test\0data'), (2, NULL); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --tab=$MYSQLTEST_VARDIR/tmp/ test t_no_hex +--echo # Check SQL file - should NOT have HEX() wrapper +--cat_file $MYSQLTEST_VARDIR/tmp/t_no_hex.sql +--echo # Check data file - blob data NOT hex-encoded (binary content) +--cat_file $MYSQLTEST_VARDIR/tmp/t_no_hex.txt +--remove_file $MYSQLTEST_VARDIR/tmp/t_no_hex.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_no_hex.txt + +DROP TABLE t_no_hex; + +--echo # Test 5: Table with only BLOB columns +CREATE TABLE t_all_blobs ( + blob1 BLOB, + blob2 TINYBLOB, + blob3 MEDIUMBLOB +); + +INSERT INTO t_all_blobs VALUES + (X'ABCDEF', X'123456', X'FEDCBA'), + (NULL, NULL, NULL); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_all_blobs +--echo # Check SQL file +--cat_file $MYSQLTEST_VARDIR/tmp/t_all_blobs.sql +--echo # Check data file +--cat_file $MYSQLTEST_VARDIR/tmp/t_all_blobs.txt +--remove_file $MYSQLTEST_VARDIR/tmp/t_all_blobs.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_all_blobs.txt + +DROP TABLE t_all_blobs; + +--echo # Test 6: Empty table with BLOB columns +CREATE TABLE t_empty_blobs ( + id INT, + data BLOB +); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --tab=$MYSQLTEST_VARDIR/tmp/ test t_empty_blobs +--echo # Check SQL file +--cat_file $MYSQLTEST_VARDIR/tmp/t_empty_blobs.sql +--echo # Check data file - should be empty +--cat_file $MYSQLTEST_VARDIR/tmp/t_empty_blobs.txt +--remove_file $MYSQLTEST_VARDIR/tmp/t_empty_blobs.sql +--remove_file $MYSQLTEST_VARDIR/tmp/t_empty_blobs.txt + +DROP TABLE t_empty_blobs; + +--echo # +--echo # Test --hex-blob with --dir option +--echo # --dir creates subdirectories for each database +--echo # + +--echo # Test 7: Basic BLOB types with --hex-blob and --dir +CREATE TABLE t_dir_hex_blob ( + id INT PRIMARY KEY, + tiny_blob TINYBLOB, + normal_blob BLOB, + medium_blob MEDIUMBLOB, + long_blob LONGBLOB +); + +INSERT INTO t_dir_hex_blob VALUES + (1, 'tiny\0data', 'normal\0blob', 'medium\0blob', 'long\0blob'), + (2, NULL, NULL, NULL, NULL), + (3, '', '', '', ''), + (4, X'DEADBEEF', X'CAFEBABE', X'FEEDFACE', X'BAADF00D'); + +--mkdir $MYSQLTEST_VARDIR/tmp/backup_dir +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --dir=$MYSQLTEST_VARDIR/tmp/backup_dir test +--echo # Check SQL file in test subdirectory +--cat_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_hex_blob.sql +--echo # Check data file - blob data should be hex-encoded +--cat_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_hex_blob.txt +--remove_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_hex_blob.sql +--remove_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_hex_blob.txt + +DROP TABLE t_dir_hex_blob; + +--echo # Test 8: Mixed table with --dir and --hex-blob +CREATE TABLE t_dir_mixed ( + id INT PRIMARY KEY, + name VARCHAR(50), + data BLOB, + binary_data VARBINARY(50) +); + +INSERT INTO t_dir_mixed VALUES + (1, 'First', X'ABCD', X'1234'), + (2, 'Second', NULL, NULL), + (3, 'Third', '', ''); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --hex-blob --dir=$MYSQLTEST_VARDIR/tmp/backup_dir test +--echo # Check SQL file +--cat_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_mixed.sql +--echo # Check data file - only BLOB/BINARY columns should be hex-encoded +--cat_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_mixed.txt +--remove_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_mixed.sql +--remove_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_mixed.txt + +DROP TABLE t_dir_mixed; + +--echo # Test 9: Verify --dir without --hex-blob (default behavior) +CREATE TABLE t_dir_no_hex ( + id INT, + blob_col BLOB +); + +INSERT INTO t_dir_no_hex VALUES (1, 'test\0data'), (2, NULL); + +--exec $MYSQL_DUMP --default-character-set=utf8mb4 --skip-comments --dir=$MYSQLTEST_VARDIR/tmp/backup_dir test +--echo # Check SQL file - should NOT have HEX() wrapper +--cat_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_no_hex.sql +--echo # Check data file - blob data NOT hex-encoded +--cat_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_no_hex.txt +--remove_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_no_hex.sql +--remove_file $MYSQLTEST_VARDIR/tmp/backup_dir/test/t_dir_no_hex.txt +--rmdir $MYSQLTEST_VARDIR/tmp/backup_dir/test +--rmdir $MYSQLTEST_VARDIR/tmp/backup_dir + +DROP TABLE t_dir_no_hex;