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