1 /*
2    Copyright (c) 2000, 2015, Oracle and/or its affiliates.
3    Copyright (c) 2011, 2017, MariaDB
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; version 2 of the License.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA
17 */
18 
19 /*
20 **	   mysqlimport.c  - Imports all given files
21 **			    into a table(s).
22 **
23 **			   *************************
24 **			   *			   *
25 **			   * AUTHOR: Monty & Jani  *
26 **			   * DATE:   June 24, 1997 *
27 **			   *			   *
28 **			   *************************
29 */
30 #define IMPORT_VERSION "3.7"
31 
32 #include "client_priv.h"
33 #include <my_sys.h>
34 
35 #include "mysql_version.h"
36 
37 #include <welcome_copyright_notice.h>   /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
38 
39 
40 /* Global Thread counter */
41 uint counter= 0;
42 pthread_mutex_t init_mutex;
43 pthread_mutex_t counter_mutex;
44 pthread_cond_t count_threshhold;
45 
46 static void db_error_with_table(MYSQL *mysql, char *table);
47 static void db_error(MYSQL *mysql);
48 static char *field_escape(char *to,const char *from,uint length);
49 static char *add_load_option(char *ptr,const char *object,
50 			     const char *statement);
51 
52 static my_bool	verbose=0,lock_tables=0,ignore_errors=0,opt_delete=0,
53                 replace, silent, ignore, ignore_foreign_keys,
54                 opt_compress, opt_low_priority, tty_password;
55 static my_bool debug_info_flag= 0, debug_check_flag= 0;
56 static uint opt_use_threads=0, opt_local_file=0, my_end_arg= 0;
57 static char	*opt_password=0, *current_user=0,
58 		*current_host=0, *current_db=0, *fields_terminated=0,
59 		*lines_terminated=0, *enclosed=0, *opt_enclosed=0,
60 		*escaped=0, *opt_columns=0,
61 		*default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME;
62 static uint     opt_mysql_port= 0, opt_protocol= 0;
63 static char * opt_mysql_unix_port=0;
64 static char *opt_plugin_dir= 0, *opt_default_auth= 0;
65 static longlong opt_ignore_lines= -1;
66 #include <sslopt-vars.h>
67 
68 static char **argv_to_free;
69 
70 static struct my_option my_long_options[] =
71 {
72   {"character-sets-dir", OPT_CHARSETS_DIR,
73    "Directory for character set files.", (char**) &charsets_dir,
74    (char**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
75   {"default-character-set", OPT_DEFAULT_CHARSET,
76    "Set the default character set.", &default_charset,
77    &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
78   {"columns", 'c',
79    "Use only these columns to import the data to. Give the column names in a comma separated list. This is same as giving columns to LOAD DATA INFILE.",
80    &opt_columns, &opt_columns, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
81    0, 0, 0},
82   {"compress", 'C', "Use compression in server/client protocol.",
83    &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
84    0, 0, 0},
85   {"debug",'#', "Output debug log. Often this is 'd:t:o,filename'.", 0, 0, 0,
86    GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
87   {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
88    &debug_check_flag, &debug_check_flag, 0,
89    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
90   {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
91    &debug_info_flag, &debug_info_flag,
92    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
93   {"default_auth", OPT_DEFAULT_AUTH,
94    "Default authentication client-side plugin to use.",
95    &opt_default_auth, &opt_default_auth, 0,
96    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
97   {"delete", 'd', "First delete all rows from table.", &opt_delete,
98    &opt_delete, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
99   {"fields-terminated-by", OPT_FTB,
100    "Fields in the input file are terminated by the given string.",
101    &fields_terminated, &fields_terminated, 0,
102    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
103   {"fields-enclosed-by", OPT_ENC,
104    "Fields in the import file are enclosed by the given character.",
105    &enclosed, &enclosed, 0,
106    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
107   {"fields-optionally-enclosed-by", OPT_O_ENC,
108    "Fields in the input file are optionally enclosed by the given character.",
109    &opt_enclosed, &opt_enclosed, 0,
110    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
111   {"fields-escaped-by", OPT_ESC,
112    "Fields in the input file are escaped by the given character.",
113    &escaped, &escaped, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0,
114    0, 0},
115   {"force", 'f', "Continue even if we get an SQL error.",
116    &ignore_errors, &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0,
117    0, 0, 0, 0},
118   {"help", '?', "Displays this help and exits.", 0, 0, 0, GET_NO_ARG, NO_ARG,
119    0, 0, 0, 0, 0, 0},
120   {"host", 'h', "Connect to host.", &current_host,
121    &current_host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
122   {"ignore", 'i', "If duplicate unique key was found, keep old row.",
123    &ignore, &ignore, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
124   {"ignore-foreign-keys", 'k',
125     "Disable foreign key checks while importing the data.",
126     &ignore_foreign_keys, &ignore_foreign_keys, 0, GET_BOOL, NO_ARG,
127     0, 0, 0, 0, 0, 0},
128   {"ignore-lines", OPT_IGN_LINES, "Ignore first n lines of data infile.",
129    &opt_ignore_lines, &opt_ignore_lines, 0, GET_LL,
130    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
131   {"lines-terminated-by", OPT_LTB,
132    "Lines in the input file are terminated by the given string.",
133    &lines_terminated, &lines_terminated, 0, GET_STR,
134    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
135   {"local", 'L', "Read all files through the client.", &opt_local_file,
136    &opt_local_file, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
137   {"lock-tables", 'l', "Lock all tables for write (this disables threads).",
138     &lock_tables, &lock_tables, 0, GET_BOOL, NO_ARG,
139     0, 0, 0, 0, 0, 0},
140   {"low-priority", OPT_LOW_PRIORITY,
141    "Use LOW_PRIORITY when updating the table.", &opt_low_priority,
142    &opt_low_priority, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
143   {"password", 'p',
144    "Password to use when connecting to server. If password is not given it's asked from the tty.",
145    0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
146 #ifdef __WIN__
147   {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
148    NO_ARG, 0, 0, 0, 0, 0, 0},
149 #endif
150   {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
151    &opt_plugin_dir, &opt_plugin_dir, 0,
152    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
153   {"port", 'P', "Port number to use for connection or 0 for default to, in "
154    "order of preference, my.cnf, $MYSQL_TCP_PORT, "
155 #if MYSQL_PORT_DEFAULT == 0
156    "/etc/services, "
157 #endif
158    "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
159    &opt_mysql_port,
160    &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,
161    0},
162   {"protocol", OPT_MYSQL_PROTOCOL, "The protocol to use for connection (tcp, socket, pipe).",
163    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
164   {"replace", 'r', "If duplicate unique key was found, replace old row.",
165    &replace, &replace, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
166   {"silent", 's', "Be more silent.", &silent, &silent, 0,
167    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
168   {"socket", 'S', "The socket file to use for connection.",
169    &opt_mysql_unix_port, &opt_mysql_unix_port, 0, GET_STR,
170    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
171 #include <sslopt-longopts.h>
172   {"use-threads", OPT_USE_THREADS,
173    "Load files in parallel. The argument is the number "
174    "of threads to use for loading data.",
175    &opt_use_threads, &opt_use_threads, 0,
176    GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
177 #ifndef DONT_ALLOW_USER_CHANGE
178   {"user", 'u', "User for login if not current user.", &current_user,
179    &current_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
180 #endif
181   {"verbose", 'v', "Print info about the various stages.", &verbose,
182    &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
183   {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
184    NO_ARG, 0, 0, 0, 0, 0, 0},
185   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
186 };
187 
188 
189 static const char *load_default_groups[]=
190 { "mysqlimport", "mariadb-import", "client", "client-server", "client-mariadb",
191   0 };
192 
193 
print_version(void)194 static void print_version(void)
195 {
196   printf("%s  Ver %s Distrib %s, for %s (%s)\n" ,my_progname,
197 	  IMPORT_VERSION, MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
198 }
199 
200 
usage(void)201 static void usage(void)
202 {
203   puts("Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.");
204   puts("Copyright 2008-2011 Oracle and Monty Program Ab.");
205   puts("Copyright 2012-2019 MariaDB Corporation Ab.");
206   print_version();
207   puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
208   printf("\
209 Loads tables from text files in various formats.  The base name of the\n\
210 text file must be the name of the table that should be used.\n\
211 If one uses sockets to connect to the MariaDB server, the server will open\n\
212 and read the text file directly. In other cases the client will open the text\n\
213 file. The SQL command 'LOAD DATA INFILE' is used to import the rows.\n");
214 
215   printf("\nUsage: %s [OPTIONS] database textfile...\n",my_progname);
216   print_defaults("my",load_default_groups);
217   puts("");
218   my_print_help(my_long_options);
219   my_print_variables(my_long_options);
220 }
221 
222 
223 static my_bool
get_one_option(const struct my_option * opt,const char * argument,const char * filename)224 get_one_option(const struct my_option *opt, const char *argument,
225                const char *filename __attribute__((unused)))
226 {
227   switch(opt->id) {
228   case 'p':
229     if (argument == disabled_my_option)
230       argument= (char*) "";			/* Don't require password */
231     if (argument)
232     {
233       /*
234         One should not really change the argument, but we make an
235         exception for passwords
236       */
237       char *start= (char*) argument;
238       my_free(opt_password);
239       opt_password=my_strdup(PSI_NOT_INSTRUMENTED, argument,MYF(MY_FAE));
240       while (*argument)
241         *(char*) argument++= 'x';               /* Destroy argument */
242       if (*start)
243 	start[1]=0;				/* Cut length of argument */
244       tty_password= 0;
245     }
246     else
247       tty_password= 1;
248     break;
249 #ifdef __WIN__
250   case 'W':
251     opt_protocol = MYSQL_PROTOCOL_PIPE;
252     opt_local_file=1;
253     break;
254 #endif
255   case OPT_MYSQL_PROTOCOL:
256     if ((opt_protocol= find_type_with_warning(argument, &sql_protocol_typelib,
257                                               opt->name)) <= 0)
258     {
259       sf_leaking_memory= 1; /* no memory leak reports here */
260       exit(1);
261     }
262     break;
263   case '#':
264     DBUG_PUSH(argument ? argument : "d:t:o");
265     debug_check_flag= 1;
266     break;
267 #include <sslopt-case.h>
268   case 'V': print_version(); exit(0);
269   case 'I':
270   case '?':
271     usage();
272     exit(0);
273   }
274   return 0;
275 }
276 
277 
get_options(int * argc,char *** argv)278 static int get_options(int *argc, char ***argv)
279 {
280   int ho_error;
281 
282   if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
283     exit(ho_error);
284   if (debug_info_flag)
285     my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
286   if (debug_check_flag)
287     my_end_arg= MY_CHECK_ERROR;
288 
289   if (enclosed && opt_enclosed)
290   {
291     fprintf(stderr, "You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n");
292     return(1);
293   }
294   if (replace && ignore)
295   {
296     fprintf(stderr, "You can't use --ignore (-i) and --replace (-r) at the same time.\n");
297     return(1);
298   }
299   if (*argc < 2)
300   {
301     usage();
302     return 1;
303   }
304   current_db= *((*argv)++);
305   (*argc)--;
306   if (tty_password)
307     opt_password=get_tty_password(NullS);
308   return(0);
309 }
310 
311 
312 
write_to_table(char * filename,MYSQL * mysql)313 static int write_to_table(char *filename, MYSQL *mysql)
314 {
315   char tablename[FN_REFLEN], hard_path[FN_REFLEN],
316        escaped_name[FN_REFLEN * 2 + 1],
317        sql_statement[FN_REFLEN*16+256], *end, *pos;
318   DBUG_ENTER("write_to_table");
319   DBUG_PRINT("enter",("filename: %s",filename));
320 
321   fn_format(tablename, filename, "", "", 1 | 2); /* removes path & ext. */
322   if (!opt_local_file)
323     strmov(hard_path,filename);
324   else
325     my_load_path(hard_path, filename, NULL); /* filename includes the path */
326 
327   if (opt_delete)
328   {
329     if (verbose)
330       fprintf(stdout, "Deleting the old data from table %s\n", tablename);
331 #ifdef HAVE_SNPRINTF
332     snprintf(sql_statement, FN_REFLEN*16+256, "DELETE FROM %s", tablename);
333 #else
334     sprintf(sql_statement, "DELETE FROM %s", tablename);
335 #endif
336     if (mysql_query(mysql, sql_statement))
337     {
338       db_error_with_table(mysql, tablename);
339       DBUG_RETURN(1);
340     }
341   }
342   to_unix_path(hard_path);
343   if (verbose)
344   {
345     if (opt_local_file)
346       fprintf(stdout, "Loading data from LOCAL file: %s into %s\n",
347 	      hard_path, tablename);
348     else
349       fprintf(stdout, "Loading data from SERVER file: %s into %s\n",
350 	      hard_path, tablename);
351   }
352   mysql_real_escape_string(mysql, escaped_name, hard_path,
353                            (unsigned long) strlen(hard_path));
354   sprintf(sql_statement, "LOAD DATA %s %s INFILE '%s'",
355 	  opt_low_priority ? "LOW_PRIORITY" : "",
356 	  opt_local_file ? "LOCAL" : "", escaped_name);
357   end= strend(sql_statement);
358   if (replace)
359     end= strmov(end, " REPLACE");
360   if (ignore)
361     end= strmov(end, " IGNORE");
362   end= strmov(end, " INTO TABLE `");
363   /* Turn any ` into `` in table name. */
364   for (pos= tablename; *pos; pos++)
365   {
366     if (*pos == '`')
367       *end++= '`';
368     *end++= *pos;
369   }
370   end= strmov(end, "`");
371 
372   if (fields_terminated || enclosed || opt_enclosed || escaped)
373       end= strmov(end, " FIELDS");
374   end= add_load_option(end, fields_terminated, " TERMINATED BY");
375   end= add_load_option(end, enclosed, " ENCLOSED BY");
376   end= add_load_option(end, opt_enclosed,
377 		       " OPTIONALLY ENCLOSED BY");
378   end= add_load_option(end, escaped, " ESCAPED BY");
379   end= add_load_option(end, lines_terminated, " LINES TERMINATED BY");
380   if (opt_ignore_lines >= 0)
381     end= strmov(longlong10_to_str(opt_ignore_lines,
382 				  strmov(end, " IGNORE "),10), " LINES");
383   if (opt_columns)
384     end= strmov(strmov(strmov(end, " ("), opt_columns), ")");
385   *end= '\0';
386 
387   if (mysql_query(mysql, sql_statement))
388   {
389     db_error_with_table(mysql, tablename);
390     DBUG_RETURN(1);
391   }
392   if (!silent)
393   {
394     if (mysql_info(mysql)) /* If NULL-pointer, print nothing */
395     {
396       fprintf(stdout, "%s.%s: %s\n", current_db, tablename,
397 	      mysql_info(mysql));
398     }
399   }
400   DBUG_RETURN(0);
401 }
402 
403 
404 
lock_table(MYSQL * mysql,int tablecount,char ** raw_tablename)405 static void lock_table(MYSQL *mysql, int tablecount, char **raw_tablename)
406 {
407   DYNAMIC_STRING query;
408   int i;
409   char tablename[FN_REFLEN];
410 
411   if (verbose)
412     fprintf(stdout, "Locking tables for write\n");
413   init_dynamic_string(&query, "LOCK TABLES ", 256, 1024);
414   for (i=0 ; i < tablecount ; i++)
415   {
416     fn_format(tablename, raw_tablename[i], "", "", 1 | 2);
417     dynstr_append(&query, tablename);
418     dynstr_append(&query, " WRITE,");
419   }
420   if (mysql_real_query(mysql, query.str, (ulong)query.length-1))
421     db_error(mysql); /* We shall countinue here, if --force was given */
422 }
423 
424 
425 
426 
db_connect(char * host,char * database,char * user,char * passwd)427 static MYSQL *db_connect(char *host, char *database,
428                          char *user, char *passwd)
429 {
430   MYSQL *mysql;
431   my_bool reconnect;
432   if (verbose)
433     fprintf(stdout, "Connecting to %s\n", host ? host : "localhost");
434   if (opt_use_threads && !lock_tables)
435   {
436     pthread_mutex_lock(&init_mutex);
437     if (!(mysql= mysql_init(NULL)))
438     {
439       pthread_mutex_unlock(&init_mutex);
440       return 0;
441     }
442     pthread_mutex_unlock(&init_mutex);
443   }
444   else
445     if (!(mysql= mysql_init(NULL)))
446       return 0;
447   if (opt_compress)
448     mysql_options(mysql,MYSQL_OPT_COMPRESS,NullS);
449   if (opt_local_file)
450     mysql_options(mysql,MYSQL_OPT_LOCAL_INFILE,
451 		  (char*) &opt_local_file);
452 #ifdef HAVE_OPENSSL
453   if (opt_use_ssl)
454   {
455     mysql_ssl_set(mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
456 		  opt_ssl_capath, opt_ssl_cipher);
457     mysql_options(mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
458     mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
459     mysql_options(mysql, MARIADB_OPT_TLS_VERSION, opt_tls_version);
460   }
461   mysql_options(mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
462                 (char*)&opt_ssl_verify_server_cert);
463 #endif
464   if (opt_protocol)
465     mysql_options(mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
466 
467   if (opt_plugin_dir && *opt_plugin_dir)
468     mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
469 
470   if (opt_default_auth && *opt_default_auth)
471     mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
472   if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
473     default_charset= (char *)my_default_csname();
474   mysql_options(mysql, MYSQL_SET_CHARSET_NAME, my_default_csname());
475   mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
476   mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
477                  "program_name", "mysqlimport");
478   if (!(mysql_real_connect(mysql,host,user,passwd,
479                            database,opt_mysql_port,opt_mysql_unix_port,
480                            0)))
481   {
482     ignore_errors=0;	  /* NO RETURN FROM db_error */
483     db_error(mysql);
484   }
485   reconnect= 0;
486   mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect);
487   if (verbose)
488     fprintf(stdout, "Selecting database %s\n", database);
489   if (mysql_select_db(mysql, database))
490   {
491     ignore_errors=0;
492     db_error(mysql);
493   }
494   if (ignore_foreign_keys)
495     mysql_query(mysql, "set foreign_key_checks= 0;");
496 
497   return mysql;
498 }
499 
500 
501 
db_disconnect(char * host,MYSQL * mysql)502 static void db_disconnect(char *host, MYSQL *mysql)
503 {
504   if (verbose)
505     fprintf(stdout, "Disconnecting from %s\n", host ? host : "localhost");
506   mysql_close(mysql);
507 }
508 
509 
safe_exit(int error,MYSQL * mysql)510 static void safe_exit(int error, MYSQL *mysql)
511 {
512   if (error && ignore_errors)
513     return;
514 
515   /* in multi-threaded mode protect from concurrent safe_exit's */
516   if (counter)
517     pthread_mutex_lock(&counter_mutex);
518 
519   if (mysql)
520     mysql_close(mysql);
521 
522   mysql_library_end();
523   free_defaults(argv_to_free);
524   my_free(opt_password);
525   if (error)
526     sf_leaking_memory= 1; /* dirty exit, some threads are still running */
527   else
528     my_end(my_end_arg); /* clean exit */
529   exit(error);
530 }
531 
532 
533 
db_error_with_table(MYSQL * mysql,char * table)534 static void db_error_with_table(MYSQL *mysql, char *table)
535 {
536   my_printf_error(0,"Error: %d, %s, when using table: %s",
537 		  MYF(0), mysql_errno(mysql), mysql_error(mysql), table);
538   safe_exit(1, mysql);
539 }
540 
541 
542 
db_error(MYSQL * mysql)543 static void db_error(MYSQL *mysql)
544 {
545   my_printf_error(0,"Error: %d %s", MYF(0), mysql_errno(mysql), mysql_error(mysql));
546   safe_exit(1, mysql);
547 }
548 
549 
add_load_option(char * ptr,const char * object,const char * statement)550 static char *add_load_option(char *ptr, const char *object,
551 			     const char *statement)
552 {
553   if (object)
554   {
555     /* Don't escape hex constants */
556     if (object[0] == '0' && (object[1] == 'x' || object[1] == 'X'))
557       ptr= strxmov(ptr," ",statement," ",object,NullS);
558     else
559     {
560       /* char constant; escape */
561       ptr= strxmov(ptr," ",statement," '",NullS);
562       ptr= field_escape(ptr,object,(uint) strlen(object));
563       *ptr++= '\'';
564     }
565   }
566   return ptr;
567 }
568 
569 /*
570 ** Allow the user to specify field terminator strings like:
571 ** "'", "\", "\\" (escaped backslash), "\t" (tab), "\n" (newline)
572 ** This is done by doubleing ' and add a end -\ if needed to avoid
573 ** syntax errors from the SQL parser.
574 */
575 
field_escape(char * to,const char * from,uint length)576 static char *field_escape(char *to,const char *from,uint length)
577 {
578   const char *end;
579   uint end_backslashes=0;
580 
581   for (end= from+length; from != end; from++)
582   {
583     *to++= *from;
584     if (*from == '\\')
585       end_backslashes^=1;    /* find odd number of backslashes */
586     else
587     {
588       if (*from == '\'' && !end_backslashes)
589 	*to++= *from;      /* We want a duplicate of "'" for MySQL */
590       end_backslashes=0;
591     }
592   }
593   /* Add missing backslashes if user has specified odd number of backs.*/
594   if (end_backslashes)
595     *to++= '\\';
596   return to;
597 }
598 
599 int exitcode= 0;
600 
worker_thread(void * arg)601 pthread_handler_t worker_thread(void *arg)
602 {
603   int error;
604   char *raw_table_name= (char *)arg;
605   MYSQL *mysql= 0;
606 
607   if (mysql_thread_init())
608     goto error;
609 
610   if (!(mysql= db_connect(current_host,current_db,current_user,opt_password)))
611   {
612     goto error;
613   }
614 
615   if (mysql_query(mysql, "/*!40101 set @@character_set_database=binary */;"))
616   {
617     db_error(mysql); /* We shall countinue here, if --force was given */
618     goto error;
619   }
620 
621   /*
622     We are not currently catching the error here.
623   */
624   if((error= write_to_table(raw_table_name, mysql)))
625     if (exitcode == 0)
626       exitcode= error;
627 
628 error:
629   if (mysql)
630     db_disconnect(current_host, mysql);
631 
632   pthread_mutex_lock(&counter_mutex);
633   counter--;
634   pthread_cond_signal(&count_threshhold);
635   pthread_mutex_unlock(&counter_mutex);
636   mysql_thread_end();
637   pthread_exit(0);
638   return 0;
639 }
640 
641 
main(int argc,char ** argv)642 int main(int argc, char **argv)
643 {
644   int error=0;
645   MY_INIT(argv[0]);
646   sf_leaking_memory=1; /* don't report memory leaks on early exits */
647 
648   load_defaults_or_exit("my", load_default_groups, &argc, &argv);
649   /* argv is changed in the program */
650   argv_to_free= argv;
651   if (get_options(&argc, &argv))
652   {
653     free_defaults(argv_to_free);
654     return(1);
655   }
656   sf_leaking_memory=0; /* from now on we cleanup properly */
657 
658   if (opt_use_threads && !lock_tables)
659   {
660     char **save_argv;
661     uint worker_thread_count= 0, table_count= 0, i= 0;
662     pthread_t *worker_threads;       /* Thread descriptor */
663     pthread_attr_t attr;             /* Thread attributes */
664     pthread_attr_init(&attr);
665     pthread_attr_setdetachstate(&attr,
666                                 PTHREAD_CREATE_JOINABLE);
667 
668     pthread_mutex_init(&init_mutex, NULL);
669     pthread_mutex_init(&counter_mutex, NULL);
670     pthread_cond_init(&count_threshhold, NULL);
671 
672     /* Count the number of tables. This number denotes the total number
673        of threads spawn.
674     */
675     save_argv= argv;
676     for (table_count= 0; *argv != NULL; argv++)
677       table_count++;
678     argv= save_argv;
679 
680     if (!(worker_threads= (pthread_t*) my_malloc(PSI_NOT_INSTRUMENTED,
681                                table_count * sizeof(*worker_threads), MYF(0))))
682       return -2;
683 
684     for (; *argv != NULL; argv++) /* Loop through tables */
685     {
686       pthread_mutex_lock(&counter_mutex);
687       while (counter == opt_use_threads)
688       {
689         struct timespec abstime;
690 
691         set_timespec(abstime, 3);
692         pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime);
693       }
694       /* Before exiting the lock we set ourselves up for the next thread */
695       counter++;
696       pthread_mutex_unlock(&counter_mutex);
697       /* now create the thread */
698       if (pthread_create(&worker_threads[worker_thread_count], &attr,
699                          worker_thread, (void *)*argv) != 0)
700       {
701         pthread_mutex_lock(&counter_mutex);
702         counter--;
703         pthread_mutex_unlock(&counter_mutex);
704         fprintf(stderr,"%s: Could not create thread\n", my_progname);
705         continue;
706       }
707       worker_thread_count++;
708     }
709 
710     /*
711       We loop until we know that all children have cleaned up.
712     */
713     pthread_mutex_lock(&counter_mutex);
714     while (counter)
715     {
716       struct timespec abstime;
717 
718       set_timespec(abstime, 3);
719       pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime);
720     }
721     pthread_mutex_unlock(&counter_mutex);
722     pthread_mutex_destroy(&init_mutex);
723     pthread_mutex_destroy(&counter_mutex);
724     pthread_cond_destroy(&count_threshhold);
725     pthread_attr_destroy(&attr);
726 
727     for(i= 0; i < worker_thread_count; i++)
728     {
729       if (pthread_join(worker_threads[i], NULL))
730         fprintf(stderr,"%s: Could not join worker thread.\n", my_progname);
731     }
732 
733     my_free(worker_threads);
734   }
735   else
736   {
737     MYSQL *mysql= 0;
738     if (!(mysql= db_connect(current_host,current_db,current_user,opt_password)))
739     {
740       free_defaults(argv_to_free);
741       return(1); /* purecov: deadcode */
742     }
743 
744     if (mysql_query(mysql, "/*!40101 set @@character_set_database=binary */;"))
745     {
746       db_error(mysql); /* We shall countinue here, if --force was given */
747       return(1);
748     }
749 
750     if (lock_tables)
751       lock_table(mysql, argc, argv);
752     for (; *argv != NULL; argv++)
753       if ((error= write_to_table(*argv, mysql)))
754         if (exitcode == 0)
755           exitcode= error;
756     db_disconnect(current_host, mysql);
757   }
758   safe_exit(0, 0);
759   return(exitcode);
760 }
761