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