~drizzle-trunk/drizzle/development

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