diff options
Diffstat (limited to 'client/mysqldump.c')
-rw-r--r-- | client/mysqldump.c | 452 |
1 files changed, 382 insertions, 70 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c index 7fae3c023b1..a01912ee7a1 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1002,6 +1002,192 @@ static int mysql_query_with_error_report(MYSQL *mysql_con, MYSQL_RES **res, return 0; } + +static int fetch_db_collation(const char *db_name, + char *db_cl_name, + int db_cl_size) +{ + bool err_status= FALSE; + char query[QUERY_LENGTH]; + MYSQL_RES *db_cl_res; + MYSQL_ROW db_cl_row; + + my_snprintf(query, sizeof (query), "use %s", db_name); + + if (mysql_query_with_error_report(mysql, NULL, query)) + return 1; + + if (mysql_query_with_error_report(mysql, &db_cl_res, + "select @@collation_database")) + return 1; + + do + { + if (mysql_num_rows(db_cl_res) != 1) + { + err_status= TRUE; + break; + } + + if (!(db_cl_row= mysql_fetch_row(db_cl_res))) + { + err_status= TRUE; + break; + } + + strncpy(db_cl_name, db_cl_row[0], db_cl_size); + db_cl_name[db_cl_size - 1]= 0; /* just in case. */ + + } while (FALSE); + + mysql_free_result(db_cl_res); + + return err_status ? 1 : 0; +} + + +static char *my_case_str(const char *str, + uint str_len, + const char *token, + uint token_len) +{ + my_match_t match; + + uint status= my_charset_latin1.coll->instr(&my_charset_latin1, + str, str_len, + token, token_len, + &match, 1); + + return status ? (char *) str + match.end : NULL; +} + + +static int switch_db_collation(FILE *sql_file, + const char *db_name, + const char *delimiter, + const char *current_db_cl_name, + const char *required_db_cl_name) +{ + if (strcmp(current_db_cl_name, required_db_cl_name) != 0) + { + CHARSET_INFO *db_cl= get_charset_by_name(required_db_cl_name, MYF(0)); + + fprintf(sql_file, + "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n", + (const char *) db_name, + (const char *) db_cl->csname, + (const char *) db_cl->name, + (const char *) delimiter); + + return 1; + } + + return 0; +} + + +static void restore_db_collation(FILE *sql_file, + const char *db_name, + const char *delimiter, + const char *db_cl_name) +{ + CHARSET_INFO *db_cl= get_charset_by_name(db_cl_name, MYF(0)); + + fprintf(sql_file, + "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n", + (const char *) db_name, + (const char *) db_cl->csname, + (const char *) db_cl->name, + (const char *) delimiter); +} + + +static void switch_cs_variables(FILE *sql_file, + const char *delimiter, + const char *character_set_client, + const char *character_set_results, + const char *collation_connection) +{ + fprintf(sql_file, + "/*!50003 SET @saved_cs_client = @@character_set_client */ %s\n" + "/*!50003 SET @saved_cs_results = @@character_set_results */ %s\n" + "/*!50003 SET @saved_col_connection = @@collation_connection */ %s\n" + "/*!50003 SET character_set_client = %s */ %s\n" + "/*!50003 SET character_set_results = %s */ %s\n" + "/*!50003 SET collation_connection = %s */ %s\n", + (const char *) delimiter, + (const char *) delimiter, + (const char *) delimiter, + + (const char *) character_set_client, + (const char *) delimiter, + + (const char *) character_set_results, + (const char *) delimiter, + + (const char *) collation_connection, + (const char *) delimiter); +} + + +static void restore_cs_variables(FILE *sql_file, + const char *delimiter) +{ + fprintf(sql_file, + "/*!50003 SET character_set_client = @saved_cs_client */ %s\n" + "/*!50003 SET character_set_results = @saved_cs_results */ %s\n" + "/*!50003 SET collation_connection = @saved_col_connection */ %s\n", + (const char *) delimiter, + (const char *) delimiter, + (const char *) delimiter); +} + + +static void switch_sql_mode(FILE *sql_file, + const char *delimiter, + const char *sql_mode) +{ + fprintf(sql_file, + "/*!50003 SET @saved_sql_mode = @@sql_mode */ %s\n" + "/*!50003 SET sql_mode = '%s' */ %s\n", + (const char *) delimiter, + + (const char *) sql_mode, + (const char *) delimiter); +} + + +static void restore_sql_mode(FILE *sql_file, + const char *delimiter) +{ + fprintf(sql_file, + "/*!50003 SET sql_mode = @saved_sql_mode */ %s\n", + (const char *) delimiter); +} + + +static void switch_time_zone(FILE *sql_file, + const char *delimiter, + const char *time_zone) +{ + fprintf(sql_file, + "/*!50003 SET @saved_time_zone = @@time_zone */ %s\n" + "/*!50003 SET time_zone = '%s' */ %s\n", + (const char *) delimiter, + + (const char *) time_zone, + (const char *) delimiter); +} + + +static void restore_time_zone(FILE *sql_file, + const char *delimiter) +{ + fprintf(sql_file, + "/*!50003 SET time_zone = @saved_time_zone */ %s\n", + (const char *) delimiter); +} + /* Open a new .sql file to dump the table or view into @@ -1470,10 +1656,14 @@ static uint dump_events_for_db(char *db) char query_buff[QUERY_LENGTH]; char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3]; char *event_name; - char delimiter[QUERY_LENGTH], *delimit_test; + char delimiter[QUERY_LENGTH]; FILE *sql_file= md_result_file; MYSQL_RES *event_res, *event_list_res; MYSQL_ROW row, event_list_row; + + char db_cl_name[MY_CS_NAME_SIZE]; + int db_cl_altered; + DBUG_ENTER("dump_events_for_db"); DBUG_PRINT("enter", ("db: '%s'", db)); @@ -1498,6 +1688,11 @@ static uint dump_events_for_db(char *db) { fprintf(sql_file, "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;\n"); + /* Get database collation. */ + + if (fetch_db_collation(db_name_buff, db_cl_name, sizeof (db_cl_name))) + DBUG_RETURN(1); + while ((event_list_row= mysql_fetch_row(event_list_res)) != NULL) { event_name= quote_name(event_list_row[1], name_buff, 0); @@ -1520,17 +1715,41 @@ static uint dump_events_for_db(char *db) fprintf(sql_file, "/*!50106 DROP EVENT IF EXISTS %s */%s\n", event_name, delimiter); - delimit_test= create_delimiter(row[3], delimiter, sizeof(delimiter)); - if (delimit_test == NULL) { - fprintf(stderr, "%s: Warning: Can't dump event '%s'\n", - event_name, my_progname); + if (create_delimiter(row[3], delimiter, sizeof(delimiter)) == NULL) + { + fprintf(stderr, "%s: Warning: Can't create delimiter for event '%s'\n", + event_name, my_progname); DBUG_RETURN(1); } fprintf(sql_file, "DELIMITER %s\n", delimiter); - fprintf(sql_file, "/*!50106 SET TIME_ZONE= '%s' */ %s\n", - row[2], delimiter); - fprintf(sql_file, "/*!50106 %s */ %s\n", row[3], delimiter); + + db_cl_altered= switch_db_collation(sql_file, + db_name_buff, + delimiter, + db_cl_name, + row[6]); + + switch_cs_variables(sql_file, delimiter, + row[4], /* character_set_client */ + row[4], /* character_set_results */ + row[5]); /* collation_connection */ + + switch_sql_mode(sql_file, delimiter, row[1]); + + switch_time_zone(sql_file, delimiter, row[2]); + + fprintf(sql_file, + "/*!50106 %s */ %s\n", + (const char *) row[3], + (const char *) delimiter); + + restore_time_zone(sql_file, delimiter); + restore_sql_mode(sql_file, delimiter); + restore_cs_variables(sql_file, delimiter); + + if (db_cl_altered) + restore_db_collation(sql_file, db_name_buff, delimiter, db_cl_name); } } /* end of event printing */ mysql_free_result(event_res); @@ -1592,6 +1811,10 @@ static uint dump_routines_for_db(char *db) FILE *sql_file= md_result_file; MYSQL_RES *routine_res, *routine_list_res; MYSQL_ROW row, routine_list_row; + + char db_cl_name[MY_CS_NAME_SIZE]; + int db_cl_altered; + DBUG_ENTER("dump_routines_for_db"); DBUG_PRINT("enter", ("db: '%s'", db)); @@ -1608,10 +1831,10 @@ static uint dump_routines_for_db(char *db) if (lock_tables) mysql_query(mysql, "LOCK TABLES mysql.proc READ"); - if (opt_compact) - fprintf(sql_file, "\n/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;\n"); + /* Get database collation. */ - fprintf(sql_file, "DELIMITER ;;\n"); + if (fetch_db_collation(db_name_buff, db_cl_name, sizeof (db_cl_name))) + DBUG_RETURN(1); /* 0, retrieve and dump functions, 1, procedures */ for (i= 0; i <= 1; i++) @@ -1658,26 +1881,34 @@ static uint dump_routines_for_db(char *db) char *definer_begin; if (opt_drop) - fprintf(sql_file, "/*!50003 DROP %s IF EXISTS %s */;;\n", + fprintf(sql_file, "/*!50003 DROP %s IF EXISTS %s */;\n", routine_type[i], routine_name); /* Cover DEFINER-clause in version-specific comments. TODO: this is definitely a BAD IDEA to parse SHOW CREATE output. - We should user INFORMATION_SCHEMA instead. The only problem is - that now INFORMATION_SCHEMA does not provide information about - routine parameters. + However, we can not use INFORMATION_SCHEMA instead: + 1. INFORMATION_SCHEMA provides data in UTF8, but here we + need data in the original character set; + 2. INFORMATION_SCHEMA does not provide information about + routine parameters now. */ - definer_begin= strstr(row[2], " DEFINER"); + definer_begin= my_case_str(row[2], strlen(row[2]), + C_STRING_WITH_LEN(" DEFINER")); if (definer_begin) { - char *definer_end= strstr(definer_begin, " PROCEDURE"); + char *definer_end= my_case_str(definer_begin, + strlen(definer_begin), + C_STRING_WITH_LEN(" PROCEDURE")); if (!definer_end) - definer_end= strstr(definer_begin, " FUNCTION"); + { + definer_end= my_case_str(definer_begin, strlen(definer_begin), + C_STRING_WITH_LEN(" FUNCTION")); + } if (definer_end) { @@ -1703,13 +1934,28 @@ static uint dump_routines_for_db(char *db) we need to change sql_mode only for the CREATE PROCEDURE/FUNCTION otherwise we may need to re-quote routine_name */ - fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=\"%s\"*/;;\n", - row[1] /* sql_mode */); - fprintf(sql_file, "/*!50003 %s */;;\n", - (query_str != NULL ? query_str : row[2])); + + db_cl_altered= switch_db_collation(sql_file, db_name_buff, ";", + db_cl_name, row[5]); + + switch_cs_variables(sql_file, ";", + row[3], /* character_set_client */ + row[3], /* character_set_results */ + row[4]); /* collation_connection */ + + switch_sql_mode(sql_file, ";", row[1]); + fprintf(sql_file, - "/*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/" - ";;\n"); + "DELIMITER ;;\n" + "/*!50003 %s */;;\n" + "DELIMITER ;\n", + (const char *) (query_str != NULL ? query_str : row[2])); + + restore_sql_mode(sql_file, ";"); + restore_cs_variables(sql_file, ";"); + + if (db_cl_altered) + restore_db_collation(sql_file, db_name_buff, ";", db_cl_name); my_free(query_str, MYF(MY_ALLOW_ZERO_PTR)); } @@ -1720,8 +1966,6 @@ static uint dump_routines_for_db(char *db) } mysql_free_result(routine_list_res); } /* end of for i (0 .. 1) */ - /* set the delimiter back to ';' */ - fprintf(sql_file, "DELIMITER ;\n"); if (lock_tables) VOID(mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES")); @@ -2239,8 +2483,7 @@ continue_xml: */ -static void dump_triggers_for_table(char *table, - char *db __attribute__((unused))) +static void dump_triggers_for_table(char *table, char *db_name) { char *result_table; char name_buff[NAME_LEN*4+3], table_buff[NAME_LEN*2+3]; @@ -2249,8 +2492,12 @@ static void dump_triggers_for_table(char *table, FILE *sql_file= md_result_file; MYSQL_RES *result; MYSQL_ROW row; + + char db_cl_name[MY_CS_NAME_SIZE]; + int db_cl_altered; + DBUG_ENTER("dump_triggers_for_table"); - DBUG_PRINT("enter", ("db: %s, table: %s", db, table)); + DBUG_PRINT("enter", ("db: %s, table: %s", db_name, table)); /* Do not use ANSI_QUOTES on triggers in dump */ opt_compatible_mode&= ~MASK_ANSI_QUOTES; @@ -2266,62 +2513,109 @@ static void dump_triggers_for_table(char *table, my_fclose(sql_file, MYF(MY_WME)); DBUG_VOID_RETURN; } - if (mysql_num_rows(result)) - { - if (opt_compact) - fprintf(sql_file, "\n/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;\n"); - fprintf(sql_file, "\nDELIMITER ;;\n"); - } + + /* Get database collation. */ + + if (fetch_db_collation(db_name, db_cl_name, sizeof (db_cl_name))) + DBUG_VOID_RETURN; + + /* Dump triggers. */ + while ((row= mysql_fetch_row(result))) { - fprintf(sql_file, - "/*!50003 SET SESSION SQL_MODE=\"%s\" */;;\n" - "/*!50003 CREATE */ ", - row[6] /* sql_mode */); + MYSQL_RES *res2; + + my_snprintf(query_buff, sizeof (query_buff), + "SHOW CREATE TRIGGER %s", + quote_name(row[0], name_buff, TRUE)); - if (mysql_num_fields(result) > 7) + if (mysql_query_with_error_report(mysql, &res2, query_buff)) { + if (path) + my_fclose(sql_file, MYF(MY_WME)); + maybe_exit(EX_MYSQLERR); + DBUG_VOID_RETURN; + } + + while ((row= mysql_fetch_row(res2))) + { + char *query_str= NULL; + char *definer_begin; + /* - mysqldump can be run against the server, that does not support definer - in triggers (there is no DEFINER column in SHOW TRIGGERS output). So, - we should check if we have this column before accessing it. + Cover DEFINER-clause in version-specific comments. + + TODO: this is definitely a BAD IDEA to parse SHOW CREATE output. + However, we can not use INFORMATION_SCHEMA instead: + 1. INFORMATION_SCHEMA provides data in UTF8, but here we + need data in the original character set; + 2. INFORMATION_SCHEMA does not provide information about + routine parameters now. */ - size_t user_name_len; - char user_name_str[USERNAME_LENGTH + 1]; - char quoted_user_name_str[USERNAME_LENGTH * 2 + 3]; - size_t host_name_len; - char host_name_str[HOSTNAME_LENGTH + 1]; - char quoted_host_name_str[HOSTNAME_LENGTH * 2 + 3]; + definer_begin= my_case_str(row[2], strlen(row[2]), + C_STRING_WITH_LEN(" DEFINER")); - parse_user(row[7], strlen(row[7]), user_name_str, &user_name_len, - host_name_str, &host_name_len); + if (definer_begin) + { + char *definer_end= my_case_str(definer_begin, strlen(definer_begin), + C_STRING_WITH_LEN(" TRIGGER")); + + if (definer_end) + { + char *query_str_tail; + + /* + Allocate memory for new query string: original string + from SHOW statement and version-specific comments. + */ + query_str= alloc_query_str(strlen(row[2]) + 23); + + query_str_tail= strnmov(query_str, row[2], + definer_begin - row[2]); + query_str_tail= strmov(query_str_tail, "*/ /*!50017"); + query_str_tail= strnmov(query_str_tail, definer_begin, + definer_end - definer_begin); + query_str_tail= strxmov(query_str_tail, "*/ /*!50003", + definer_end, NullS); + } + } + + db_cl_altered= switch_db_collation(sql_file, db_name, ";", + db_cl_name, row[5]); + + switch_cs_variables(sql_file, ";", + row[3], /* character_set_client */ + row[3], /* character_set_results */ + row[4]); /* collation_connection */ + + switch_sql_mode(sql_file, ";", row[1]); fprintf(sql_file, - "/*!50017 DEFINER=%s@%s */ ", - quote_name(user_name_str, quoted_user_name_str, FALSE), - quote_name(host_name_str, quoted_host_name_str, FALSE)); - } + "DELIMITER ;;\n" + "/*!50003 %s */;;\n" + "DELIMITER ;\n", + (const char *) (query_str != NULL ? query_str : row[2])); - fprintf(sql_file, - "/*!50003 TRIGGER %s %s %s ON %s FOR EACH ROW%s%s */;;\n\n", - quote_name(row[0], name_buff, 0), /* Trigger */ - row[4], /* Timing */ - row[1], /* Event */ - result_table, - (strchr(" \t\n\r", *(row[3]))) ? "" : " ", - row[3] /* Statement */); + restore_sql_mode(sql_file, ";"); + restore_cs_variables(sql_file, ";"); + + if (db_cl_altered) + restore_db_collation(sql_file, db_name, ";", db_cl_name); + + my_free(query_str, MYF(MY_ALLOW_ZERO_PTR)); + } + mysql_free_result(res2); } - if (mysql_num_rows(result)) - fprintf(sql_file, - "DELIMITER ;\n" - "/*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE */;\n"); + mysql_free_result(result); + /* make sure to set back opt_compatible mode to original value */ opt_compatible_mode=old_opt_compatible_mode; + DBUG_VOID_RETURN; } @@ -4107,9 +4401,11 @@ static my_bool get_view_structure(char *table, char* db) my_snprintf(query, sizeof(query), - "SELECT CHECK_OPTION, DEFINER, SECURITY_TYPE " \ - "FROM information_schema.views " \ + "SELECT CHECK_OPTION, DEFINER, SECURITY_TYPE, " + " CHARACTER_SET_CLIENT, COLLATION_CONNECTION " + "FROM information_schema.views " "WHERE table_name=\"%s\" AND table_schema=\"%s\"", table, db); + if (mysql_query(mysql, query)) { /* @@ -4195,7 +4491,23 @@ static my_bool get_view_structure(char *table, char* db) } /* Dump view structure to file */ - fprintf(sql_file, "/*!50001 %s */;\n", ds_view.str); + + fprintf(sql_file, + "/*!50001 SET @saved_cs_client = @@character_set_client */;\n" + "/*!50001 SET @saved_cs_results = @@character_set_results */;\n" + "/*!50001 SET @saved_col_connection = @@collation_connection */;\n" + "/*!50001 SET character_set_client = %s */;\n" + "/*!50001 SET character_set_results = %s */;\n" + "/*!50001 SET collation_connection = %s */;\n" + "/*!50001 %s */;\n" + "/*!50001 SET character_set_client = @saved_cs_client */;\n" + "/*!50001 SET character_set_results = @saved_cs_results */;\n" + "/*!50001 SET collation_connection = @saved_col_connection */;\n", + (const char *) row[3], + (const char *) row[3], + (const char *) row[4], + (const char *) ds_view.str); + check_io(sql_file); mysql_free_result(table_res); dynstr_free(&ds_view); |