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(int optid,const struct my_option * opt,char * argument)224 get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
225 	       char *argument)
226 {
227   switch(optid) {
228   case 'p':
229     if (argument == disabled_my_option)
230       argument= (char*) "";			/* Don't require password */
231     if (argument)
232     {
233       char *start=argument;
234       my_free(opt_password);
235       opt_password=my_strdup(argument,MYF(MY_FAE));
236       while (*argument) *argument++= 'x';		/* Destroy argument */
237       if (*start)
238 	start[1]=0;				/* Cut length of argument */
239       tty_password= 0;
240     }
241     else
242       tty_password= 1;
243     break;
244 #ifdef __WIN__
245   case 'W':
246     opt_protocol = MYSQL_PROTOCOL_PIPE;
247     opt_local_file=1;
248     break;
249 #endif
250   case OPT_MYSQL_PROTOCOL:
251     if ((opt_protocol= find_type_with_warning(argument, &sql_protocol_typelib,
252                                               opt->name)) <= 0)
253     {
254       sf_leaking_memory= 1; /* no memory leak reports here */
255       exit(1);
256     }
257     break;
258   case '#':
259     DBUG_PUSH(argument ? argument : "d:t:o");
260     debug_check_flag= 1;
261     break;
262 #include <sslopt-case.h>
263   case 'V': print_version(); exit(0);
264   case 'I':
265   case '?':
266     usage();
267     exit(0);
268   }
269   return 0;
270 }
271 
272 
get_options(int * argc,char *** argv)273 static int get_options(int *argc, char ***argv)
274 {
275   int ho_error;
276 
277   if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
278     exit(ho_error);
279   if (debug_info_flag)
280     my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
281   if (debug_check_flag)
282     my_end_arg= MY_CHECK_ERROR;
283 
284   if (enclosed && opt_enclosed)
285   {
286     fprintf(stderr, "You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n");
287     return(1);
288   }
289   if (replace && ignore)
290   {
291     fprintf(stderr, "You can't use --ignore (-i) and --replace (-r) at the same time.\n");
292     return(1);
293   }
294   if (*argc < 2)
295   {
296     usage();
297     return 1;
298   }
299   current_db= *((*argv)++);
300   (*argc)--;
301   if (tty_password)
302     opt_password=get_tty_password(NullS);
303   return(0);
304 }
305 
306 
307 
write_to_table(char * filename,MYSQL * mysql)308 static int write_to_table(char *filename, MYSQL *mysql)
309 {
310   char tablename[FN_REFLEN], hard_path[FN_REFLEN],
311        escaped_name[FN_REFLEN * 2 + 1],
312        sql_statement[FN_REFLEN*16+256], *end, *pos;
313   DBUG_ENTER("write_to_table");
314   DBUG_PRINT("enter",("filename: %s",filename));
315 
316   fn_format(tablename, filename, "", "", 1 | 2); /* removes path & ext. */
317   if (!opt_local_file)
318     strmov(hard_path,filename);
319   else
320     my_load_path(hard_path, filename, NULL); /* filename includes the path */
321 
322   if (opt_delete)
323   {
324     if (verbose)
325       fprintf(stdout, "Deleting the old data from table %s\n", tablename);
326 #ifdef HAVE_SNPRINTF
327     snprintf(sql_statement, FN_REFLEN*16+256, "DELETE FROM %s", tablename);
328 #else
329     sprintf(sql_statement, "DELETE FROM %s", tablename);
330 #endif
331     if (mysql_query(mysql, sql_statement))
332     {
333       db_error_with_table(mysql, tablename);
334       DBUG_RETURN(1);
335     }
336   }
337   to_unix_path(hard_path);
338   if (verbose)
339   {
340     if (opt_local_file)
341       fprintf(stdout, "Loading data from LOCAL file: %s into %s\n",
342 	      hard_path, tablename);
343     else
344       fprintf(stdout, "Loading data from SERVER file: %s into %s\n",
345 	      hard_path, tablename);
346   }
347   mysql_real_escape_string(mysql, escaped_name, hard_path,
348                            (unsigned long) strlen(hard_path));
349   sprintf(sql_statement, "LOAD DATA %s %s INFILE '%s'",
350 	  opt_low_priority ? "LOW_PRIORITY" : "",
351 	  opt_local_file ? "LOCAL" : "", escaped_name);
352   end= strend(sql_statement);
353   if (replace)
354     end= strmov(end, " REPLACE");
355   if (ignore)
356     end= strmov(end, " IGNORE");
357   end= strmov(end, " INTO TABLE `");
358   /* Turn any ` into `` in table name. */
359   for (pos= tablename; *pos; pos++)
360   {
361     if (*pos == '`')
362       *end++= '`';
363     *end++= *pos;
364   }
365   end= strmov(end, "`");
366 
367   if (fields_terminated || enclosed || opt_enclosed || escaped)
368       end= strmov(end, " FIELDS");
369   end= add_load_option(end, fields_terminated, " TERMINATED BY");
370   end= add_load_option(end, enclosed, " ENCLOSED BY");
371   end= add_load_option(end, opt_enclosed,
372 		       " OPTIONALLY ENCLOSED BY");
373   end= add_load_option(end, escaped, " ESCAPED BY");
374   end= add_load_option(end, lines_terminated, " LINES TERMINATED BY");
375   if (opt_ignore_lines >= 0)
376     end= strmov(longlong10_to_str(opt_ignore_lines,
377 				  strmov(end, " IGNORE "),10), " LINES");
378   if (opt_columns)
379     end= strmov(strmov(strmov(end, " ("), opt_columns), ")");
380   *end= '\0';
381 
382   if (mysql_query(mysql, sql_statement))
383   {
384     db_error_with_table(mysql, tablename);
385     DBUG_RETURN(1);
386   }
387   if (!silent)
388   {
389     if (mysql_info(mysql)) /* If NULL-pointer, print nothing */
390     {
391       fprintf(stdout, "%s.%s: %s\n", current_db, tablename,
392 	      mysql_info(mysql));
393     }
394   }
395   DBUG_RETURN(0);
396 }
397 
398 
399 
lock_table(MYSQL * mysql,int tablecount,char ** raw_tablename)400 static void lock_table(MYSQL *mysql, int tablecount, char **raw_tablename)
401 {
402   DYNAMIC_STRING query;
403   int i;
404   char tablename[FN_REFLEN];
405 
406   if (verbose)
407     fprintf(stdout, "Locking tables for write\n");
408   init_dynamic_string(&query, "LOCK TABLES ", 256, 1024);
409   for (i=0 ; i < tablecount ; i++)
410   {
411     fn_format(tablename, raw_tablename[i], "", "", 1 | 2);
412     dynstr_append(&query, tablename);
413     dynstr_append(&query, " WRITE,");
414   }
415   if (mysql_real_query(mysql, query.str, (ulong)query.length-1))
416     db_error(mysql); /* We shall countinue here, if --force was given */
417 }
418 
419 
420 
421 
db_connect(char * host,char * database,char * user,char * passwd)422 static MYSQL *db_connect(char *host, char *database,
423                          char *user, char *passwd)
424 {
425   MYSQL *mysql;
426   my_bool reconnect;
427   if (verbose)
428     fprintf(stdout, "Connecting to %s\n", host ? host : "localhost");
429   if (opt_use_threads && !lock_tables)
430   {
431     pthread_mutex_lock(&init_mutex);
432     if (!(mysql= mysql_init(NULL)))
433     {
434       pthread_mutex_unlock(&init_mutex);
435       return 0;
436     }
437     pthread_mutex_unlock(&init_mutex);
438   }
439   else
440     if (!(mysql= mysql_init(NULL)))
441       return 0;
442   if (opt_compress)
443     mysql_options(mysql,MYSQL_OPT_COMPRESS,NullS);
444   if (opt_local_file)
445     mysql_options(mysql,MYSQL_OPT_LOCAL_INFILE,
446 		  (char*) &opt_local_file);
447 #ifdef HAVE_OPENSSL
448   if (opt_use_ssl)
449   {
450     mysql_ssl_set(mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
451 		  opt_ssl_capath, opt_ssl_cipher);
452     mysql_options(mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
453     mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
454     mysql_options(mysql, MARIADB_OPT_TLS_VERSION, opt_tls_version);
455   }
456   mysql_options(mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
457                 (char*)&opt_ssl_verify_server_cert);
458 #endif
459   if (opt_protocol)
460     mysql_options(mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
461 
462   if (opt_plugin_dir && *opt_plugin_dir)
463     mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
464 
465   if (opt_default_auth && *opt_default_auth)
466     mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
467   if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
468     default_charset= (char *)my_default_csname();
469   mysql_options(mysql, MYSQL_SET_CHARSET_NAME, my_default_csname());
470   mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
471   mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
472                  "program_name", "mysqlimport");
473   if (!(mysql_real_connect(mysql,host,user,passwd,
474                            database,opt_mysql_port,opt_mysql_unix_port,
475                            0)))
476   {
477     ignore_errors=0;	  /* NO RETURN FROM db_error */
478     db_error(mysql);
479   }
480   reconnect= 0;
481   mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect);
482   if (verbose)
483     fprintf(stdout, "Selecting database %s\n", database);
484   if (mysql_select_db(mysql, database))
485   {
486     ignore_errors=0;
487     db_error(mysql);
488   }
489   if (ignore_foreign_keys)
490     mysql_query(mysql, "set foreign_key_checks= 0;");
491 
492   return mysql;
493 }
494 
495 
496 
db_disconnect(char * host,MYSQL * mysql)497 static void db_disconnect(char *host, MYSQL *mysql)
498 {
499   if (verbose)
500     fprintf(stdout, "Disconnecting from %s\n", host ? host : "localhost");
501   mysql_close(mysql);
502 }
503 
504 
safe_exit(int error,MYSQL * mysql)505 static void safe_exit(int error, MYSQL *mysql)
506 {
507   if (error && ignore_errors)
508     return;
509 
510   /* in multi-threaded mode protect from concurrent safe_exit's */
511   if (counter)
512     pthread_mutex_lock(&counter_mutex);
513 
514   if (mysql)
515     mysql_close(mysql);
516 
517   mysql_library_end();
518   free_defaults(argv_to_free);
519   my_free(opt_password);
520   if (error)
521     sf_leaking_memory= 1; /* dirty exit, some threads are still running */
522   else
523     my_end(my_end_arg); /* clean exit */
524   exit(error);
525 }
526 
527 
528 
db_error_with_table(MYSQL * mysql,char * table)529 static void db_error_with_table(MYSQL *mysql, char *table)
530 {
531   my_printf_error(0,"Error: %d, %s, when using table: %s",
532 		  MYF(0), mysql_errno(mysql), mysql_error(mysql), table);
533   safe_exit(1, mysql);
534 }
535 
536 
537 
db_error(MYSQL * mysql)538 static void db_error(MYSQL *mysql)
539 {
540   my_printf_error(0,"Error: %d %s", MYF(0), mysql_errno(mysql), mysql_error(mysql));
541   safe_exit(1, mysql);
542 }
543 
544 
add_load_option(char * ptr,const char * object,const char * statement)545 static char *add_load_option(char *ptr, const char *object,
546 			     const char *statement)
547 {
548   if (object)
549   {
550     /* Don't escape hex constants */
551     if (object[0] == '0' && (object[1] == 'x' || object[1] == 'X'))
552       ptr= strxmov(ptr," ",statement," ",object,NullS);
553     else
554     {
555       /* char constant; escape */
556       ptr= strxmov(ptr," ",statement," '",NullS);
557       ptr= field_escape(ptr,object,(uint) strlen(object));
558       *ptr++= '\'';
559     }
560   }
561   return ptr;
562 }
563 
564 /*
565 ** Allow the user to specify field terminator strings like:
566 ** "'", "\", "\\" (escaped backslash), "\t" (tab), "\n" (newline)
567 ** This is done by doubleing ' and add a end -\ if needed to avoid
568 ** syntax errors from the SQL parser.
569 */
570 
field_escape(char * to,const char * from,uint length)571 static char *field_escape(char *to,const char *from,uint length)
572 {
573   const char *end;
574   uint end_backslashes=0;
575 
576   for (end= from+length; from != end; from++)
577   {
578     *to++= *from;
579     if (*from == '\\')
580       end_backslashes^=1;    /* find odd number of backslashes */
581     else
582     {
583       if (*from == '\'' && !end_backslashes)
584 	*to++= *from;      /* We want a duplicate of "'" for MySQL */
585       end_backslashes=0;
586     }
587   }
588   /* Add missing backslashes if user has specified odd number of backs.*/
589   if (end_backslashes)
590     *to++= '\\';
591   return to;
592 }
593 
594 int exitcode= 0;
595 
worker_thread(void * arg)596 pthread_handler_t worker_thread(void *arg)
597 {
598   int error;
599   char *raw_table_name= (char *)arg;
600   MYSQL *mysql= 0;
601 
602   if (mysql_thread_init())
603     goto error;
604 
605   if (!(mysql= db_connect(current_host,current_db,current_user,opt_password)))
606   {
607     goto error;
608   }
609 
610   if (mysql_query(mysql, "/*!40101 set @@character_set_database=binary */;"))
611   {
612     db_error(mysql); /* We shall countinue here, if --force was given */
613     goto error;
614   }
615 
616   /*
617     We are not currently catching the error here.
618   */
619   if((error= write_to_table(raw_table_name, mysql)))
620     if (exitcode == 0)
621       exitcode= error;
622 
623 error:
624   if (mysql)
625     db_disconnect(current_host, mysql);
626 
627   pthread_mutex_lock(&counter_mutex);
628   counter--;
629   pthread_cond_signal(&count_threshhold);
630   pthread_mutex_unlock(&counter_mutex);
631   mysql_thread_end();
632   pthread_exit(0);
633   return 0;
634 }
635 
636 
main(int argc,char ** argv)637 int main(int argc, char **argv)
638 {
639   int error=0;
640   MY_INIT(argv[0]);
641   sf_leaking_memory=1; /* don't report memory leaks on early exits */
642 
643   load_defaults_or_exit("my", load_default_groups, &argc, &argv);
644   /* argv is changed in the program */
645   argv_to_free= argv;
646   if (get_options(&argc, &argv))
647   {
648     free_defaults(argv_to_free);
649     return(1);
650   }
651   sf_leaking_memory=0; /* from now on we cleanup properly */
652 
653   if (opt_use_threads && !lock_tables)
654   {
655     char **save_argv;
656     uint worker_thread_count= 0, table_count= 0, i= 0;
657     pthread_t *worker_threads;       /* Thread descriptor */
658     pthread_attr_t attr;             /* Thread attributes */
659     pthread_attr_init(&attr);
660     pthread_attr_setdetachstate(&attr,
661                                 PTHREAD_CREATE_JOINABLE);
662 
663     pthread_mutex_init(&init_mutex, NULL);
664     pthread_mutex_init(&counter_mutex, NULL);
665     pthread_cond_init(&count_threshhold, NULL);
666 
667     /* Count the number of tables. This number denotes the total number
668        of threads spawn.
669     */
670     save_argv= argv;
671     for (table_count= 0; *argv != NULL; argv++)
672       table_count++;
673     argv= save_argv;
674 
675     if (!(worker_threads= (pthread_t*) my_malloc(table_count *
676                                                  sizeof(*worker_threads),
677                                                  MYF(0))))
678       return -2;
679 
680     for (; *argv != NULL; argv++) /* Loop through tables */
681     {
682       pthread_mutex_lock(&counter_mutex);
683       while (counter == opt_use_threads)
684       {
685         struct timespec abstime;
686 
687         set_timespec(abstime, 3);
688         pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime);
689       }
690       /* Before exiting the lock we set ourselves up for the next thread */
691       counter++;
692       pthread_mutex_unlock(&counter_mutex);
693       /* now create the thread */
694       if (pthread_create(&worker_threads[worker_thread_count], &attr,
695                          worker_thread, (void *)*argv) != 0)
696       {
697         pthread_mutex_lock(&counter_mutex);
698         counter--;
699         pthread_mutex_unlock(&counter_mutex);
700         fprintf(stderr,"%s: Could not create thread\n", my_progname);
701         continue;
702       }
703       worker_thread_count++;
704     }
705 
706     /*
707       We loop until we know that all children have cleaned up.
708     */
709     pthread_mutex_lock(&counter_mutex);
710     while (counter)
711     {
712       struct timespec abstime;
713 
714       set_timespec(abstime, 3);
715       pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime);
716     }
717     pthread_mutex_unlock(&counter_mutex);
718     pthread_mutex_destroy(&init_mutex);
719     pthread_mutex_destroy(&counter_mutex);
720     pthread_cond_destroy(&count_threshhold);
721     pthread_attr_destroy(&attr);
722 
723     for(i= 0; i < worker_thread_count; i++)
724     {
725       if (pthread_join(worker_threads[i], NULL))
726         fprintf(stderr,"%s: Could not join worker thread.\n", my_progname);
727     }
728 
729     my_free(worker_threads);
730   }
731   else
732   {
733     MYSQL *mysql= 0;
734     if (!(mysql= db_connect(current_host,current_db,current_user,opt_password)))
735     {
736       free_defaults(argv_to_free);
737       return(1); /* purecov: deadcode */
738     }
739 
740     if (mysql_query(mysql, "/*!40101 set @@character_set_database=binary */;"))
741     {
742       db_error(mysql); /* We shall countinue here, if --force was given */
743       return(1);
744     }
745 
746     if (lock_tables)
747       lock_table(mysql, argc, argv);
748     for (; *argv != NULL; argv++)
749       if ((error= write_to_table(*argv, mysql)))
750         if (exitcode == 0)
751           exitcode= error;
752     db_disconnect(current_host, mysql);
753   }
754   safe_exit(0, 0);
755   return(exitcode);
756 }
757