diff options
Diffstat (limited to 'client/mysqldump.c')
-rw-r--r-- | client/mysqldump.c | 1285 |
1 files changed, 1285 insertions, 0 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c new file mode 100644 index 00000000000..ce687d815d8 --- /dev/null +++ b/client/mysqldump.c @@ -0,0 +1,1285 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* mysqldump.c - Dump a tables contents and format to an ASCII file +** +** The author's original notes follow :- +** +** ****************************************************** +** * * +** * AUTHOR: Igor Romanenko (igor@frog.kiev.ua) * +** * DATE: December 3, 1994 * +** * WARRANTY: None, expressed, impressed, implied * +** * or other * +** * STATUS: Public domain * +** * Adapted and optimized for MySQL by * +** * Michael Widenius, Sinisa Milivojevic, Jani Tolonen * +** * -w --where added 9/10/98 by Jim Faucette * +** * slave code by David Saez Padros <david@ols.es> * +** * * +** ****************************************************** +*/ +/* SSL by +** Andrei Errapart <andreie@no.spam.ee> +** Tõnu Samuel <tonu@please.do.not.remove.this.spam.ee> +**/ + +#define DUMP_VERSION "8.8" + +#include <global.h> +#include <my_sys.h> +#include <m_string.h> +#include <m_ctype.h> + +#include "mysql.h" +#include "mysql_version.h" +#include "mysqld_error.h" +#include <getopt.h> + +/* Exit codes */ + +#define EX_USAGE 1 +#define EX_MYSQLERR 2 +#define EX_CONSCHECK 3 +#define EX_EOM 4 + +/* index into 'show fields from table' */ + +#define SHOW_FIELDNAME 0 +#define SHOW_TYPE 1 +#define SHOW_NULL 2 +#define SHOW_DEFAULT 4 +#define SHOW_EXTRA 5 +#define QUOTE_CHAR '`' + +static char *add_load_option(char *ptr, const char *object, + const char *statement); + +static char *field_escape(char *to,const char *from,uint length); +static my_bool verbose=0,tFlag=0,cFlag=0,dFlag=0,quick=0, extended_insert = 0, + lock_tables=0,ignore_errors=0,flush_logs=0,replace=0, + ignore=0,opt_drop=0,opt_keywords=0,opt_lock=0,opt_compress=0, + opt_delayed=0,create_options=0,opt_quoted=0,opt_databases=0, + opt_alldbs=0,opt_create_db=0,opt_first_slave=0; +static MYSQL mysql_connection,*sock=0; +static char insert_pat[12 * 1024],*password=0,*current_user=0, + *current_host=0,*path=0,*fields_terminated=0, + *lines_terminated=0, *enclosed=0, *opt_enclosed=0, *escaped=0, + *where=0, *default_charset; +static uint opt_mysql_port=0; +static my_string opt_mysql_unix_port=0; +static int first_error=0; +extern ulong net_buffer_length; +static DYNAMIC_STRING extended_row; +#include "sslopt-vars.h" + +enum options {OPT_FTB=256, OPT_LTB, OPT_ENC, OPT_O_ENC, OPT_ESC, OPT_KEYWORDS, + OPT_LOCKS, OPT_DROP, OPT_OPTIMIZE, OPT_DELAYED, OPT_TABLES, + OPT_CHARSETS_DIR, OPT_DEFAULT_CHARSET}; + +static struct option long_options[] = +{ + {"all-databases", no_argument, 0, 'A'}, + {"all", no_argument, 0, 'a'}, + {"add-drop-table", no_argument, 0, OPT_DROP}, + {"add-locks", no_argument, 0, OPT_LOCKS}, + {"allow-keywords", no_argument, 0, OPT_KEYWORDS}, + {"character-sets-dir",required_argument,0, OPT_CHARSETS_DIR}, + {"complete-insert", no_argument, 0, 'c'}, + {"compress", no_argument, 0, 'C'}, + {"databases", no_argument, 0, 'B'}, + {"debug", optional_argument, 0, '#'}, + {"default-character-set", required_argument, 0, OPT_DEFAULT_CHARSET}, + {"delayed-insert", no_argument, 0, OPT_DELAYED}, + {"extended-insert", no_argument, 0, 'e'}, + {"fields-terminated-by", required_argument, 0, (int) OPT_FTB}, + {"fields-enclosed-by", required_argument, 0, (int) OPT_ENC}, + {"fields-optionally-enclosed-by", required_argument, 0, (int) OPT_O_ENC}, + {"fields-escaped-by", required_argument, 0, (int) OPT_ESC}, + {"first-slave", no_argument, 0, 'x'}, + {"flush-logs", no_argument, 0, 'F'}, + {"force", no_argument, 0, 'f'}, + {"help", no_argument, 0, '?'}, + {"host", required_argument, 0, 'h'}, + {"lines-terminated-by", required_argument, 0, (int) OPT_LTB}, + {"lock-tables", no_argument, 0, 'l'}, + {"no-create-db", no_argument, 0, 'n'}, + {"no-create-info", no_argument, 0, 't'}, + {"no-data", no_argument, 0, 'd'}, + {"opt", no_argument, 0, OPT_OPTIMIZE}, + {"password", optional_argument, 0, 'p'}, +#ifdef __WIN__ + {"pipe", no_argument, 0, 'W'}, +#endif + {"port", required_argument, 0, 'P'}, + {"quick", no_argument, 0, 'q'}, + {"quote-names", no_argument, 0, 'Q'}, + {"set-variable", required_argument, 0, 'O'}, + {"socket", required_argument, 0, 'S'}, +#include "sslopt-longopts.h" + {"tab", required_argument, 0, 'T'}, + {"tables", no_argument, 0, OPT_TABLES}, +#ifndef DONT_ALLOW_USER_CHANGE + {"user", required_argument, 0, 'u'}, +#endif + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"where", required_argument, 0, 'w'}, + {0, 0, 0, 0} +}; + +static const char *load_default_groups[]= { "mysqldump","client",0 }; + +CHANGEABLE_VAR changeable_vars[] = { + { "max_allowed_packet", (long*) &max_allowed_packet,24*1024*1024,4096, + 24*1024L*1024L,MALLOC_OVERHEAD,1024}, + { "net_buffer_length", (long*) &net_buffer_length,1024*1024L-1025,4096, + 24*1024L*1024L,MALLOC_OVERHEAD,1024}, + { 0, 0, 0, 0, 0, 0, 0} +}; + +static void safe_exit(int error); +static void write_heder(FILE *sql_file, char *db_name); +static void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row, + const char *prefix,const char *name, + int string_value); +static int dump_selected_tables(char *db, char **table_names, int tables); +static int dump_all_tables_in_db(char *db); +static int init_dumping(char *); +static int dump_databases(char **); +static int dump_all_databases(); +static char *quote_name(char *name, char *buff); + +static void print_version(void) +{ + printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,DUMP_VERSION, + MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); +} /* print_version */ + + +static void usage(void) +{ + uint i; + print_version(); + puts("By Igor Romanenko, Monty, Jani & Sinisa"); + puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,\nand you are welcome to modify and redistribute it under the GPL license\n"); + puts("Dumping definition and data mysql database or table"); + printf("Usage: %s [OPTIONS] database [tables]\n", my_progname); + printf("OR %s [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...]\n", + my_progname); + printf("OR %s [OPTIONS] --all-databases [OPTIONS]\n", my_progname); + printf("\n\ + -A, --all-databases Dump all the databases. This will be same as\n\ + --databases with all databases selected.\n\ + -a, --all Include all MySQL specific create options.\n\ + -#, --debug=... Output debug log. Often this is 'd:t:o,filename`.\n\ + --character-sets-dir=...\n\ + Directory where character sets are\n\ + -?, --help Display this help message and exit.\n\ + -B, --databases To dump several databases. Note the difference in\n\ + usage; In this case no tables are given. All name\n\ + arguments are regarded as databasenames.\n\ + 'USE db_name;' will be included in the output\n\ + -c, --complete-insert Use complete insert statements.\n\ + -C, --compress Use compression in server/client protocol.\n\ + --default-character-set=...\n\ + Set the default character set\n\ + -e, --extended-insert Allows utilization of the new, much faster\n\ + INSERT syntax.\n\ + --add-drop-table Add a 'drop table' before each create.\n\ + --add-locks Add locks around insert statements.\n\ + --allow-keywords Allow creation of column names that are keywords.\n\ + --delayed-insert Insert rows with INSERT DELAYED.\n\ + -F, --flush-logs Flush logs file in server before starting dump.\n\ + -f, --force Continue even if we get an sql-error.\n\ + -h, --host=... Connect to host.\n"); +puts("\ + -l, --lock-tables Lock all tables for read.\n\ + -n, --no-create-db 'CREATE DATABASE /*!32312 IF NOT EXISTS*/ db_name;'\n\ + will not be put in the output. The above line will\n\ + be added otherwise, if --databases or\n\ + --all-databases option was given.\n\ + -t, --no-create-info Don't write table creation info.\n\ + -d, --no-data No row information.\n\ + -O, --set-variable var=option\n\ + give a variable a value. --help lists variables\n\ + --opt Same as --add-drop-table --add-locks --all\n\ + --extended-insert --quick --lock-tables\n\ + -p, --password[=...] Password to use when connecting to server.\n\ + If password is not given it's solicited on the tty.\n"); +#ifdef __WIN__ + puts("-W, --pipe Use named pipes to connect to server"); +#endif + printf("\ + -P, --port=... Port number to use for connection.\n\ + -q, --quick Don't buffer query, dump directly to stdout.\n\ + -Q, --quote-names Quote table and column names with `\n\ + -S, --socket=... Socket file to use for connection.\n\ + --tables Overrides option --databases (-B).\n"); +#include "sslopt-usage.h" + printf("\ + -T, --tab=... Creates tab separated textfile for each table to\n\ + given path. (creates .sql and .txt files).\n\ + NOTE: This only works if mysqldump is run on\n\ + the same machine as the mysqld daemon.\n"); +#ifndef DONT_ALLOW_USER_CHANGE + printf("\ + -u, --user=# User for login if not current user.\n"); +#endif + printf("\ + -v, --verbose Print info about the various stages.\n\ + -V, --version Output version information and exit.\n\ + -w, --where= dump only selected records; QUOTES mandatory!\n\ + EXAMPLES: \"--where=user=\'jimf\'\" \"-wuserid>1\" \"-wuserid<1\"\n\ + Use -T (--tab=...) with --fields-...\n\ + --fields-terminated-by=...\n\ + Fields in the textfile are terminated by ...\n\ + --fields-enclosed-by=...\n\ + Fields in the importfile are enclosed by ...\n\ + --fields-optionally-enclosed-by=...\n\ + Fields in the i.file are opt. enclosed by ...\n\ + --fields-escaped-by=...\n\ + Fields in the i.file are escaped by ...\n\ + --lines-terminated-by=...\n\ + Lines in the i.file are terminated by ...\n\ +"); + print_defaults("my",load_default_groups); + + printf("\nPossible variables for option --set-variable (-O) are:\n"); + for (i=0 ; changeable_vars[i].name ; i++) + printf("%-20s current value: %lu\n", + changeable_vars[i].name, + (ulong) *changeable_vars[i].varptr); +} /* usage */ + + +static void write_heder(FILE *sql_file, char *db_name) +{ + fprintf(sql_file, "# MySQL dump %s\n#\n", DUMP_VERSION); + fprintf(sql_file, "# Host: %s Database: %s\n", + current_host ? current_host : "localhost", db_name ? db_name : ""); + fputs("#--------------------------------------------------------\n", + sql_file); + fprintf(sql_file, "# Server version\t%s\n", + mysql_get_server_info(&mysql_connection)); + return; +} /* write_heder */ + + +static int get_options(int *argc,char ***argv) +{ + int c,option_index; + my_bool tty_password=0; + + load_defaults("my",load_default_groups,argc,argv); + set_all_changeable_vars(changeable_vars); + while ((c=getopt_long(*argc,*argv,"#::p::h:u:O:P:S:T:EBaAcCdefFlnqtvVw:?Ix", + long_options, &option_index)) != EOF) + { + switch(c) { + case 'a': + create_options=1; + break; + case 'e': + extended_insert=1; + break; + case 'A': + opt_alldbs=1; + break; + case OPT_DEFAULT_CHARSET: + default_charset= optarg; + break; + case OPT_CHARSETS_DIR: + charsets_dir= optarg; + break; + case 'f': + ignore_errors=1; + break; + case 'F': + flush_logs=1; + break; + case 'h': + my_free(current_host,MYF(MY_ALLOW_ZERO_PTR)); + current_host=my_strdup(optarg,MYF(MY_WME)); + break; + case 'n': + opt_create_db = 1; + break; +#ifndef DONT_ALLOW_USER_CHANGE + case 'u': + current_user=optarg; + break; +#endif + case 'O': + if (set_changeable_var(optarg, changeable_vars)) + { + usage(); + return(1); + } + break; + case 'p': + if (optarg) + { + my_free(password,MYF(MY_ALLOW_ZERO_PTR)); + password=my_strdup(optarg,MYF(MY_FAE)); + while (*optarg) *optarg++= 'x'; /* Destroy argument */ + } + else + tty_password=1; + break; + case 'P': + opt_mysql_port= (unsigned int) atoi(optarg); + break; + case 'S': + opt_mysql_unix_port= optarg; + break; + case 'W': +#ifdef __WIN__ + opt_mysql_unix_port=MYSQL_NAMEDPIPE; +#endif + break; + case 'T': + path= optarg; + break; + case 'B': + opt_databases = 1; + break; + case '#': + DBUG_PUSH(optarg ? optarg : "d:t:o"); + break; + case 'c': cFlag=1; break; + case 'C': + opt_compress=1; + break; + case 'd': dFlag=1; break; + case 'l': lock_tables=1; break; + case 'q': quick=1; break; + case 'Q': opt_quoted=1; break; + case 't': tFlag=1; break; + case 'v': verbose=1; break; + case 'V': print_version(); exit(0); + case 'w': + where=optarg; + break; + case 'x': + opt_first_slave=1; + break; + default: + fprintf(stderr,"%s: Illegal option character '%c'\n",my_progname,opterr); + /* Fall throught */ + case 'I': + case '?': + usage(); + exit(0); + case (int) OPT_FTB: + fields_terminated= optarg; + break; + case (int) OPT_LTB: + lines_terminated= optarg; + break; + case (int) OPT_ENC: + enclosed= optarg; + break; + case (int) OPT_O_ENC: + opt_enclosed= optarg; + break; + case (int) OPT_ESC: + escaped= optarg; + break; + case (int) OPT_DROP: + opt_drop=1; + break; + case (int) OPT_KEYWORDS: + opt_keywords=1; + break; + case (int) OPT_LOCKS: + opt_lock=1; + break; + case (int) OPT_OPTIMIZE: + extended_insert=opt_drop=opt_lock=lock_tables=quick=create_options=1; + break; + case (int) OPT_DELAYED: + opt_delayed=1; + break; + case (int) OPT_TABLES: + opt_databases=0; + break; +#include "sslopt-case.h" + } + } + if (opt_delayed) + opt_lock=0; /* Can't have lock with delayed */ + if (!path && (enclosed || opt_enclosed || escaped || lines_terminated || + fields_terminated)) + { + fprintf(stderr, "%s: You must use option --tab with --fields-...\n", my_progname); + return(1); + } + + if (enclosed && opt_enclosed) + { + fprintf(stderr, "%s: You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n", my_progname); + return(1); + } + if (replace && ignore) + { + fprintf(stderr, "%s: You can't use --ignore (-i) and --replace (-r) at the same time.\n",my_progname); + return(1); + } + if ((opt_databases || opt_alldbs) && path) + { + fprintf(stderr, + "%s: --databases or --all-databases can't be used with --tab.\n", + my_progname); + return(1); + } + if (default_charset) + { + if (set_default_charset_by_name(default_charset, MYF(MY_WME))) + exit(1); + } + (*argc)-=optind; + (*argv)+=optind; + if ((*argc < 1 && !opt_alldbs) || (*argc > 0 && opt_alldbs)) + { + usage(); + return 1; + } + if (tty_password) + password=get_tty_password(NullS); + return(0); +} /* get_options */ + + +/* +** DBerror -- prints mysql error message and exits the program. +*/ +static void DBerror(MYSQL *mysql, const char *when) +{ + DBUG_ENTER("DBerror"); + my_printf_error(0,"Got error: %d: %s %s", MYF(0), + mysql_errno(mysql), mysql_error(mysql), when); + safe_exit(EX_MYSQLERR); + DBUG_VOID_RETURN; +} /* DBerror */ + + +static void safe_exit(int error) +{ + if (!first_error) + first_error= error; + if (ignore_errors) + return; + if (sock) + mysql_close(sock); + exit(error); +} +/* safe_exit */ + + +/* +** dbConnect -- connects to the host and selects DB. +** Also checks whether the tablename is a valid table name. +*/ +static int dbConnect(char *host, char *user,char *passwd) +{ + DBUG_ENTER("dbConnect"); + if (verbose) + { + fprintf(stderr, "# Connecting to %s...\n", host ? host : "localhost"); + } + mysql_init(&mysql_connection); + if (opt_compress) + mysql_options(&mysql_connection,MYSQL_OPT_COMPRESS,NullS); +#ifdef HAVE_OPENSSL + if (opt_use_ssl) + mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, + opt_ssl_capath); +#endif + if (!(sock= mysql_real_connect(&mysql_connection,host,user,passwd, + NULL,opt_mysql_port,opt_mysql_unix_port, + 0))) + { + DBerror(&mysql_connection, "when trying to connect"); + return 1; + } + return 0; +} /* dbConnect */ + + +/* +** dbDisconnect -- disconnects from the host. +*/ +static void dbDisconnect(char *host) +{ + if (verbose) + fprintf(stderr, "# Disconnecting from %s...\n", host ? host : "localhost"); + mysql_close(sock); +} /* dbDisconnect */ + + +static void unescape(FILE *file,char *pos,uint length) +{ + char *tmp; + DBUG_ENTER("unescape"); + if (!(tmp=(char*) my_malloc(length*2+1, MYF(MY_WME)))) + { + ignore_errors=0; /* Fatal error */ + safe_exit(EX_MYSQLERR); /* Force exit */ + } + mysql_real_escape_string(&mysql_connection,tmp, pos, length); + fputc('\'', file); + fputs(tmp, file); + fputc('\'', file); + my_free(tmp, MYF(MY_WME)); + DBUG_VOID_RETURN; +} /* unescape */ + + +static my_bool test_if_special_chars(const char *str) +{ +#if MYSQL_VERSION_ID >= 32300 + for ( ; *str ; str++) + if (!isvar(*str) && *str != '$') + return 1; +#endif + return 0; +} /* test_if_special_chars */ + +static char *quote_name(char *name, char *buff) +{ + char *end; + if (!opt_quoted && !test_if_special_chars(name)) + return name; + buff[0]=QUOTE_CHAR; + end=strmov(buff+1,name); + end[0]=QUOTE_CHAR; + end[1]=0; + return buff; +} /* quote_name */ + + +/* +** getStructure -- retrievs database structure, prints out corresponding +** CREATE statement and fills out insert_pat. +** Return values: number of fields in table, 0 if error +*/ +static uint getTableStructure(char *table, char* db) +{ + MYSQL_RES *tableRes; + MYSQL_ROW row; + my_bool init=0; + uint numFields; + char *strpos, *table_name; + const char *delayed; + char name_buff[NAME_LEN+3],table_buff[NAME_LEN+3]; + FILE *sql_file = stdout; + DBUG_ENTER("getTableStructure"); + + delayed= opt_delayed ? " DELAYED " : ""; + + if (verbose) + fprintf(stderr, "# Retrieving table structure for table %s...\n", table); + + table_name=quote_name(table,table_buff); + sprintf(insert_pat,"show fields from %s",table_name); + if (mysql_query(sock,insert_pat) || !(tableRes=mysql_store_result(sock))) + { + fprintf(stderr, "%s: Can't get info about table: '%s'\nerror: %s\n", + my_progname, table, mysql_error(sock)); + if (sql_file != stdout) + my_fclose(sql_file, MYF(MY_WME)); + safe_exit(EX_MYSQLERR); + DBUG_RETURN(0); + } + + /* Make an sql-file, if path was given iow. option -T was given */ + if (!tFlag) + { + if (path) + { + char filename[FN_REFLEN], tmp_path[FN_REFLEN]; + strmov(tmp_path,path); + convert_dirname(tmp_path); + sql_file= my_fopen(fn_format(filename, table, tmp_path, ".sql", 4), + O_WRONLY, MYF(MY_WME)); + if (!sql_file) /* If file couldn't be opened */ + { + safe_exit(EX_MYSQLERR); + DBUG_RETURN(0); + } + write_heder(sql_file, db); + } + fprintf(sql_file, "\n#\n# Table structure for table '%s'\n#\n\n", table); + if (opt_drop) + fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n",table_name); + fprintf(sql_file, "CREATE TABLE %s (\n", table_name); + } + if (cFlag) + sprintf(insert_pat, "INSERT %sINTO %s (", delayed, table_name); + else + { + sprintf(insert_pat, "INSERT %sINTO %s VALUES ", delayed, table_name); + if (!extended_insert) + strcat(insert_pat,"("); + } + + strpos=strend(insert_pat); + while ((row=mysql_fetch_row(tableRes))) + { + ulong *lengths=mysql_fetch_lengths(tableRes); + if (init) + { + if (!tFlag) + fputs(",\n",sql_file); + if (cFlag) + strpos=strmov(strpos,", "); + } + init=1; + if (cFlag) + strpos=strmov(strpos,quote_name(row[SHOW_FIELDNAME],name_buff)); + if (!tFlag) + { + if (opt_keywords) + fprintf(sql_file, " %s.%s %s", table_name, + quote_name(row[SHOW_FIELDNAME],name_buff), row[SHOW_TYPE]); + else + fprintf(sql_file, " %s %s", quote_name(row[SHOW_FIELDNAME],name_buff), + row[SHOW_TYPE]); + if (row[SHOW_DEFAULT]) + { + fputs(" DEFAULT ", sql_file); + unescape(sql_file,row[SHOW_DEFAULT],lengths[SHOW_DEFAULT]); + } + if (!row[SHOW_NULL][0]) + fputs(" NOT NULL", sql_file); + if (row[SHOW_EXTRA][0]) + fprintf(sql_file, " %s",row[SHOW_EXTRA]); + } + } + numFields = (uint) mysql_num_rows(tableRes); + mysql_free_result(tableRes); + if (!tFlag) + { + char buff[20+FN_REFLEN]; + uint keynr,primary_key; + sprintf(buff,"show keys from %s",table_name); + if (mysql_query(sock, buff)) + { + fprintf(stderr, "%s: Can't get keys for table '%s' (%s)\n", + my_progname, table, mysql_error(sock)); + if (sql_file != stdout) + my_fclose(sql_file, MYF(MY_WME)); + safe_exit(EX_MYSQLERR); + DBUG_RETURN(0); + } + + tableRes=mysql_store_result(sock); + /* Find first which key is primary key */ + keynr=0; + primary_key=INT_MAX; + while ((row=mysql_fetch_row(tableRes))) + { + if (atoi(row[3]) == 1) + { + keynr++; +#ifdef FORCE_PRIMARY_KEY + if (atoi(row[1]) == 0 && primary_key == INT_MAX) + primary_key=keynr; +#endif + if (!strcmp(row[2],"PRIMARY")) + { + primary_key=keynr; + break; + } + } + } + mysql_data_seek(tableRes,0); + keynr=0; + while ((row=mysql_fetch_row(tableRes))) + { + if (atoi(row[3]) == 1) + { + if (keynr++) + putc(')', sql_file); + if (atoi(row[1])) /* Test if duplicate key */ + /* Duplicate allowed */ + fprintf(sql_file, ",\n KEY %s (",quote_name(row[2],name_buff)); + else if (keynr == primary_key) + fputs(",\n PRIMARY KEY (",sql_file); /* First UNIQUE is primary */ + else + fprintf(sql_file, ",\n UNIQUE %s (",quote_name(row[2],name_buff)); + } + else + putc(',', sql_file); + fputs(quote_name(row[4],name_buff), sql_file); + if (row[7]) + fprintf(sql_file, "(%s)",row[7]); /* Sub key */ + } + if (keynr) + putc(')', sql_file); + fputs("\n)",sql_file); + + /* Get MySQL specific create options */ + if (create_options) + { + sprintf(buff,"show table status like '%s'",table); + if (mysql_query(sock, buff)) + { + if (mysql_errno(sock) != ER_PARSE_ERROR) + { /* If old MySQL version */ + if (verbose) + fprintf(stderr, + "# Warning: Couldn't get status information for table '%s' (%s)\n", + table,mysql_error(sock)); + } + } + else if (!(tableRes=mysql_store_result(sock)) || + !(row=mysql_fetch_row(tableRes))) + { + fprintf(stderr, + "Error: Couldn't read status information for table '%s' (%s)\n", + table,mysql_error(sock)); + } + else + { + fputs("/*!",sql_file); + print_value(sql_file,tableRes,row,"type=","Type",0); + print_value(sql_file,tableRes,row,"","Create_options",0); + print_value(sql_file,tableRes,row,"comment=","Comment",1); + fputs(" */",sql_file); + } + mysql_free_result(tableRes); /* Is always safe to free */ + } + fputs(";\n", sql_file); + } + if (cFlag) + { + strpos=strmov(strpos,") VALUES "); + if (!extended_insert) + strpos=strmov(strpos,"("); + } + DBUG_RETURN(numFields); +} /* getTableStructure */ + + +static char *add_load_option(char *ptr,const char *object, + const char *statement) +{ + if (object) + { + ptr= strxmov(ptr," ",statement," '",NullS); + ptr= field_escape(ptr,object,strlen(object)); + *ptr++= '\''; + } + return ptr; +} /* add_load_option */ + + +/* +** Allow the user to specify field terminator strings like: +** "'", "\", "\\" (escaped backslash), "\t" (tab), "\n" (newline) +** This is done by doubleing ' 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; + + for (end= from+length; from != end; from++) + { + *to++= *from; + if (*from == '\\') + end_backslashes^=1; /* find odd number of backslashes */ + else + { + if (*from == '\'' && !end_backslashes) + *to++= *from; /* We want a duplicate of "'" for MySQL */ + end_backslashes=0; + } + } + /* Add missing backslashes if user has specified odd number of backs.*/ + if (end_backslashes) + *to++= '\\'; + return to; +} /* field_escape */ + + +/* +** dumpTable saves database contents as a series of INSERT statements. +*/ +static void dumpTable(uint numFields, char *table) +{ + char query[1024], *end, buff[256],table_buff[NAME_LEN+3]; + MYSQL_RES *res; + MYSQL_FIELD *field; + MYSQL_ROW row; + ulong rownr, row_break, total_length, init_length; + + if (verbose) + fprintf(stderr, "# Sending SELECT query...\n"); + if (path) + { + char filename[FN_REFLEN], tmp_path[FN_REFLEN]; + strmov(tmp_path, path); + convert_dirname(tmp_path); + my_load_path(tmp_path, tmp_path, NULL); + fn_format(filename, table, tmp_path, ".txt", 4); + my_delete(filename, MYF(0)); /* 'INTO OUTFILE' doesn't work, if + filename wasn't deleted */ + to_unix_path(filename); + sprintf(query, "SELECT * INTO OUTFILE '%s'", filename); + end= strend(query); + if (replace) + end= strmov(end, " REPLACE"); + if (ignore) + end= strmov(end, " IGNORE"); + + if (fields_terminated || enclosed || opt_enclosed || escaped) + 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, escaped, " ESCAPED BY"); + end= add_load_option(end, lines_terminated, " LINES TERMINATED BY"); + *end= '\0'; + + sprintf(buff," FROM %s",table); + end= strmov(end,buff); + if (where) + end= strxmov(end, " WHERE ",where,NullS); + if (mysql_query(sock, query)) + { + DBerror(sock, "when executing 'SELECT INTO OUTFILE'"); + return; + } + } + else + { + printf("\n#\n# Dumping data for table '%s'\n", table); + sprintf(query, "SELECT * FROM %s", quote_name(table,table_buff)); + if (where) + { + printf("# WHERE: %s\n",where); + strxmov(strend(query), " WHERE ",where,NullS); + } + puts("#\n"); + + if (mysql_query(sock, query)) + { + DBerror(sock, "when retrieving data from server"); + return; + } + if (quick) + res=mysql_use_result(sock); + else + res=mysql_store_result(sock); + if (!res) + { + DBerror(sock, "when retrieving data from server"); + return; + } + if (verbose) + fprintf(stderr, "# Retrieving rows...\n"); + if (mysql_num_fields(res) != numFields) + { + fprintf(stderr,"%s: Error in field count for table: '%s' ! Aborting.\n", + my_progname,table); + safe_exit(EX_CONSCHECK); + return; + } + + if (opt_lock) + printf("LOCK TABLES %s WRITE;\n", quote_name(table,table_buff)); + + total_length=net_buffer_length; /* Force row break */ + row_break=0; + rownr=0; + init_length=strlen(insert_pat)+4; + + while ((row=mysql_fetch_row(res))) + { + uint i; + ulong *lengths=mysql_fetch_lengths(res); + rownr++; + if (!extended_insert) + fputs(insert_pat,stdout); + mysql_field_seek(res,0); + + for (i = 0; i < mysql_num_fields(res); i++) + { + if (!(field = mysql_fetch_field(res))) + { + sprintf(query,"%s: Not enough fields from table '%s'! Aborting.\n", + my_progname,table); + fputs(query,stderr); + safe_exit(EX_CONSCHECK); + return; + } + if (extended_insert) + { + ulong length = lengths[i]; + if (i == 0) + dynstr_set(&extended_row,"("); + else + dynstr_append(&extended_row,","); + + if (row[i]) + { + if (length) + { + if (!IS_NUM(field->type)) + { + if (dynstr_realloc(&extended_row,length * 2+2)) + { + fputs("Aborting dump (out of memory)",stderr); + safe_exit(EX_EOM); + } + dynstr_append(&extended_row,"\'"); + extended_row.length += + mysql_real_escape_string(&mysql_connection, + &extended_row.str[extended_row.length],row[i],length); + extended_row.str[extended_row.length]='\0'; + dynstr_append(&extended_row,"\'"); + } + else + dynstr_append(&extended_row,row[i]); + } + else + dynstr_append(&extended_row,"\'\'"); + } + else if (dynstr_append(&extended_row,"NULL")) + { + fputs("Aborting dump (out of memory)",stderr); + safe_exit(EX_EOM); + } + } + else + { + if (i) + putchar(','); + if (row[i]) + { + if (!IS_NUM(field->type)) + unescape(stdout, row[i], lengths[i]); + else + fputs(row[i],stdout); + } + else + { + fputs("NULL",stdout); + } + } + } + + if (extended_insert) + { + ulong row_length; + dynstr_append(&extended_row,")"); + row_length = 2 + extended_row.length; + if (total_length + row_length < net_buffer_length) + { + total_length += row_length; + putchar(','); /* Always row break */ + fputs(extended_row.str,stdout); + } + else + { + if (row_break) + puts(";"); + row_break=1; /* This is first row */ + fputs(insert_pat,stdout); + fputs(extended_row.str,stdout); + total_length = row_length+init_length; + } + } + else + { + puts(");"); + } + } + if (extended_insert && row_break) + puts(";"); /* If not empty table */ + fflush(stdout); + if (mysql_errno(sock)) + { + sprintf(query,"%s: Error %d: %s when dumping table '%s' at row: %ld\n", + my_progname, + mysql_errno(sock), + mysql_error(sock), + table, + rownr); + fputs(query,stderr); + safe_exit(EX_CONSCHECK); + return; + } + if (opt_lock) + puts("UNLOCK TABLES;"); + mysql_free_result(res); + } +} /* dumpTable */ + + +static char *getTableName(int reset) +{ + static MYSQL_RES *res = NULL; + MYSQL_ROW row; + + if (!res) + { + if (!(res = mysql_list_tables(sock,NullS))) + return(NULL); + } + if ((row = mysql_fetch_row(res))) + return((char*) row[0]); + + if (reset) + mysql_data_seek(res,0); /* We want to read again */ + else + { + mysql_free_result(res); + res = NULL; + } + return(NULL); +} /* getTableName */ + + +static int dump_all_databases() +{ + MYSQL_ROW row; + MYSQL_RES *tableres; + int result=0; + + if (mysql_query(sock, "SHOW DATABASES") || + !(tableres = mysql_store_result(sock))) + { + my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s", + MYF(0), mysql_error(sock)); + return 1; + } + while ((row = mysql_fetch_row(tableres))) + { + if (dump_all_tables_in_db(row[0])) + result=1; + } + return result; +} +/* dump_all_databases */ + + +static int dump_databases(char **db_names) +{ + int result=0; + for ( ; *db_names ; db_names++) + { + if (dump_all_tables_in_db(*db_names)) + result=1; + } + return result; +} /* dump_databases */ + + +static int init_dumping(char *database) +{ + if (mysql_select_db(sock, database)) + { + DBerror(sock, "when selecting the database"); + return 1; /* If --force */ + } + if (!path) + { + if (opt_databases || opt_alldbs) + { + printf("\n#\n# Current Database: %s\n#\n", database); + if (!opt_create_db) + printf("\nCREATE DATABASE /*!32312 IF NOT EXISTS*/ %s;\n", database); + printf("\nUSE %s;\n", database); + } + } + if (extended_insert) + if (init_dynamic_string(&extended_row, "", 1024, 1024)) + exit(EX_EOM); + return 0; +} /* init_dumping */ + + +static int dump_all_tables_in_db(char *database) +{ + char *table; + uint numrows; + + if (init_dumping(database)) + return 1; + if (lock_tables) + { + DYNAMIC_STRING query; + init_dynamic_string(&query, "LOCK TABLES ", 256, 1024); + for (numrows=0 ; (table = getTableName(1)) ; numrows++) + { + dynstr_append(&query, table); + dynstr_append(&query, " READ /*!32311 LOCAL */,"); + } + if (numrows && mysql_real_query(sock, query.str, query.length-1)) + DBerror(sock, "when using LOCK TABLES"); + /* We shall continue here, if --force was given */ + dynstr_free(&query); + } + if (flush_logs) + { + if (mysql_refresh(sock, REFRESH_LOG)) + DBerror(sock, "when doing refresh"); + /* We shall continue here, if --force was given */ + } + while ((table = getTableName(0))) + { + numrows = getTableStructure(table, database); + if (!dFlag && numrows > 0) + dumpTable(numrows,table); + } + if (lock_tables) + mysql_query(sock,"UNLOCK_TABLES"); + return 0; +} /* dump_all_tables_in_db */ + + + +static int dump_selected_tables(char *db, char **table_names, int tables) +{ + uint numrows; + + if (init_dumping(db)) + return 1; + if (lock_tables) + { + DYNAMIC_STRING query; + int i; + + init_dynamic_string(&query, "LOCK TABLES ", 256, 1024); + for (i=0 ; i < tables ; i++) + { + dynstr_append(&query, table_names[i]); + dynstr_append(&query, " READ /*!32311 LOCAL */,"); + } + if (mysql_real_query(sock, query.str, query.length-1)) + DBerror(sock, "when doing LOCK TABLES"); + /* We shall countinue here, if --force was given */ + dynstr_free(&query); + } + if (flush_logs) + { + if (mysql_refresh(sock, REFRESH_LOG)) + DBerror(sock, "when doing refresh"); + /* We shall countinue here, if --force was given */ + } + for (; tables > 0 ; tables-- , table_names++) + { + numrows = getTableStructure(*table_names, db); + if (!dFlag && numrows > 0) + dumpTable(numrows, *table_names); + } + if (lock_tables) + mysql_query(sock,"UNLOCK_TABLES"); + return 0; +} /* dump_selected_tables */ + + +/* Print a value with a prefix on file */ +static void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row, + const char *prefix, const char *name, + int string_value) +{ + MYSQL_FIELD *field; + mysql_field_seek(result, 0); + + for ( ; (field = mysql_fetch_field(result)) ; row++) + { + if (!strcmp(field->name,name)) + { + if (row[0] && row[0][0] && strcmp(row[0],"0")) /* Skip default */ + { + fputc(' ',file); + fputs(prefix, file); + if (string_value) + unescape(file,row[0],strlen(row[0])); + else + fputs(row[0], file); + return; + } + } + } + return; /* This shouldn't happen */ +} /* print_value */ + + +int main(int argc, char **argv) +{ + MY_INIT(argv[0]); + /* + ** Check out the args + */ + if (get_options(&argc, &argv)) + { + my_end(0); + exit(EX_USAGE); + } + if (dbConnect(current_host, current_user, password)) + exit(EX_MYSQLERR); + if (!path) + write_heder(stdout, *argv); + + if (opt_first_slave) + { + lock_tables=0; /* No other locks needed */ + if (mysql_query(sock, "FLUSH TABLES WITH READ LOCK")) + { + my_printf_error(0, "Error: Couldn't execute 'FLUSH TABLES WITH READ LOCK': %s", + MYF(0), mysql_error(sock)); + my_end(0); + return(first_error); + } + } + if (opt_alldbs) + dump_all_databases(); + /* Only one database and selected table(s) */ + else if (argc > 1 && !opt_databases) + dump_selected_tables(*argv, (argv + 1), (argc - 1)); + /* One or more databases, all tables */ + else + dump_databases(argv); + + if (opt_first_slave) + { + if (mysql_query(sock, "FLUSH MASTER")) + { + my_printf_error(0, "Error: Couldn't execute 'FLUSH MASTER': %s", + MYF(0), mysql_error(sock)); + } + if (mysql_query(sock, "UNLOCK TABLES")) + { + my_printf_error(0, "Error: Couldn't execute 'UNLOCK TABLES': %s", + MYF(0), mysql_error(sock)); + } + } + dbDisconnect(current_host); + puts(""); + my_free(password, MYF(MY_ALLOW_ZERO_PTR)); + if (extended_insert) + dynstr_free(&extended_row); + my_end(0); + return(first_error); +} /* main */ |