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.", ¤t_host,
121 ¤t_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.", ¤t_user,
179 ¤t_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