00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #define IMPORT_VERSION "4.0"
00021
00022 #include "client_priv.h"
00023 #include <string>
00024 #include <sstream>
00025 #include <iostream>
00026 #include <fstream>
00027 #include <boost/program_options.hpp>
00028 #include <pthread.h>
00029
00030 #include <drizzled/definitions.h>
00031 #include <drizzled/internal/my_sys.h>
00032
00033 #include <drizzled/gettext.h>
00034 #include <drizzled/configmake.h>
00035
00036 #include "user_detect.h"
00037
00038 namespace po= boost::program_options;
00039 using namespace std;
00040 using namespace drizzled;
00041
00042 extern "C" void * worker_thread(void *arg);
00043
00044 int exitcode= 0;
00045
00046 const char *program_name= "drizzleimport";
00047
00048
00049 uint32_t counter;
00050 pthread_mutex_t counter_mutex;
00051 pthread_cond_t count_threshhold;
00052
00053 static void db_error(drizzle_con_st *con, drizzle_result_st *result,
00054 drizzle_return_t ret, char *table);
00055 static char *field_escape(char *to,const char *from,uint32_t length);
00056 static char *add_load_option(char *ptr,const char *object,
00057 const char *statement);
00058
00059 static bool verbose= false, ignore_errors= false,
00060 opt_delete= false, opt_replace= false, silent= false,
00061 ignore_unique= false, opt_low_priority= false,
00062 use_drizzle_protocol= false, opt_local_file;
00063
00064 static uint32_t opt_use_threads;
00065 static uint32_t opt_drizzle_port= 0;
00066 static int64_t opt_ignore_lines= -1;
00067
00068 std::string opt_columns,
00069 opt_enclosed,
00070 escaped,
00071 password,
00072 current_db,
00073 lines_terminated,
00074 current_user,
00075 opt_password,
00076 enclosed,
00077 current_host,
00078 fields_terminated,
00079 opt_protocol;
00080
00081
00082 static int get_options(void)
00083 {
00084
00085 if (! enclosed.empty() && ! opt_enclosed.empty())
00086 {
00087 fprintf(stderr, "You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n");
00088 return(1);
00089 }
00090 if (opt_replace && ignore_unique)
00091 {
00092 fprintf(stderr, "You can't use --ignore_unique (-i) and --replace (-r) at the same time.\n");
00093 return(1);
00094 }
00095
00096 if (tty_password)
00097 opt_password=client_get_tty_password(NULL);
00098 return(0);
00099 }
00100
00101
00102
00103 static int write_to_table(char *filename, drizzle_con_st *con)
00104 {
00105 char tablename[FN_REFLEN], hard_path[FN_REFLEN],
00106 sql_statement[FN_REFLEN*16+256], *end;
00107 drizzle_result_st result;
00108 drizzle_return_t ret;
00109
00110 internal::fn_format(tablename, filename, "", "", 1 | 2);
00111 if (not opt_local_file)
00112 strcpy(hard_path,filename);
00113 else
00114 internal::my_load_path(hard_path, filename, NULL);
00115
00116 if (opt_delete)
00117 {
00118 if (verbose)
00119 fprintf(stdout, "Deleting the old data from table %s\n", tablename);
00120 #ifdef HAVE_SNPRINTF
00121 snprintf(sql_statement, sizeof(sql_statement), "DELETE FROM %s", tablename);
00122 #else
00123 snprintf(sql_statement, sizeof(sql_statement), "DELETE FROM %s", tablename);
00124 #endif
00125 if (drizzle_query_str(con, &result, sql_statement, &ret) == NULL ||
00126 ret != DRIZZLE_RETURN_OK)
00127 {
00128 db_error(con, &result, ret, tablename);
00129 return(1);
00130 }
00131 drizzle_result_free(&result);
00132 }
00133 if (verbose)
00134 {
00135 if (opt_local_file)
00136 fprintf(stdout, "Loading data from LOCAL file: %s into %s\n",
00137 hard_path, tablename);
00138 else
00139 fprintf(stdout, "Loading data from SERVER file: %s into %s\n",
00140 hard_path, tablename);
00141 }
00142 snprintf(sql_statement, sizeof(sql_statement), "LOAD DATA %s %s INFILE '%s'",
00143 opt_low_priority ? "LOW_PRIORITY" : "",
00144 opt_local_file ? "LOCAL" : "", hard_path);
00145 end= strchr(sql_statement, '\0');
00146 if (opt_replace)
00147 end= strcpy(end, " REPLACE")+8;
00148 if (ignore_unique)
00149 end= strcpy(end, " IGNORE")+7;
00150
00151 end+= sprintf(end, " INTO TABLE %s", tablename);
00152
00153 if (! fields_terminated.empty() || ! enclosed.empty() || ! opt_enclosed.empty() || ! escaped.empty())
00154 end= strcpy(end, " FIELDS")+7;
00155 end= add_load_option(end, (char *)fields_terminated.c_str(), " TERMINATED BY");
00156 end= add_load_option(end, (char *)enclosed.c_str(), " ENCLOSED BY");
00157 end= add_load_option(end, (char *)opt_enclosed.c_str(),
00158 " OPTIONALLY ENCLOSED BY");
00159 end= add_load_option(end, (char *)escaped.c_str(), " ESCAPED BY");
00160 end= add_load_option(end, (char *)lines_terminated.c_str(), " LINES TERMINATED BY");
00161 if (opt_ignore_lines >= 0)
00162 {
00163 end= strcpy(end, " IGNORE ")+8;
00164 ostringstream buffer;
00165 buffer << opt_ignore_lines;
00166 end= strcpy(end, buffer.str().c_str())+ buffer.str().size();
00167 end= strcpy(end, " LINES")+6;
00168 }
00169 if (! opt_columns.empty())
00170 {
00171 end= strcpy(end, " (")+2;
00172 end= strcpy(end, (char *)opt_columns.c_str()+opt_columns.length());
00173 end= strcpy(end, ")")+1;
00174 }
00175 *end= '\0';
00176
00177 if (drizzle_query_str(con, &result, sql_statement, &ret) == NULL ||
00178 ret != DRIZZLE_RETURN_OK)
00179 {
00180 db_error(con, &result, ret, tablename);
00181 return(1);
00182 }
00183 if (!silent)
00184 {
00185 if (strcmp(drizzle_result_info(&result), ""))
00186 {
00187 fprintf(stdout, "%s.%s: %s\n", current_db.c_str(), tablename,
00188 drizzle_result_info(&result));
00189 }
00190 }
00191 drizzle_result_free(&result);
00192 return(0);
00193 }
00194
00195
00196 static drizzle_con_st *db_connect(const string host, const string database,
00197 const string user, const string passwd)
00198 {
00199 drizzle_st *drizzle;
00200 drizzle_con_st *con;
00201 drizzle_return_t ret;
00202
00203 if (verbose)
00204 {
00205 fprintf(stdout, "Connecting to %s, using protocol %s...\n", ! host.empty() ? host.c_str() : "localhost", opt_protocol.c_str());
00206 }
00207
00208 if ((drizzle= drizzle_create()) == NULL)
00209 {
00210 return 0;
00211 }
00212
00213 if (!(con= drizzle_con_add_tcp(drizzle,
00214 host.c_str(), opt_drizzle_port,
00215 user.c_str(), passwd.c_str(),
00216 database.c_str(),
00217 use_drizzle_protocol ? DRIZZLE_CON_EXPERIMENTAL : DRIZZLE_CON_MYSQL)))
00218 {
00219 return 0;
00220 }
00221
00222 if ((ret= drizzle_con_connect(con)) != DRIZZLE_RETURN_OK)
00223 {
00224 ignore_errors=0;
00225 db_error(con, NULL, ret, NULL);
00226 }
00227
00228 if (verbose)
00229 fprintf(stdout, "Selecting database %s\n", database.c_str());
00230
00231 return con;
00232 }
00233
00234
00235
00236 static void db_disconnect(const string host, drizzle_con_st *con)
00237 {
00238 if (verbose)
00239 fprintf(stdout, "Disconnecting from %s\n", ! host.empty() ? host.c_str() : "localhost");
00240 drizzle_free(drizzle_con_drizzle(con));
00241 }
00242
00243
00244
00245 static void safe_exit(int error, drizzle_con_st *con)
00246 {
00247 if (ignore_errors)
00248 return;
00249 if (con)
00250 drizzle_free(drizzle_con_drizzle(con));
00251 exit(error);
00252 }
00253
00254
00255
00256 static void db_error(drizzle_con_st *con, drizzle_result_st *result,
00257 drizzle_return_t ret, char *table)
00258 {
00259 if (ret == DRIZZLE_RETURN_ERROR_CODE)
00260 {
00261 fprintf(stdout, "Error: %d, %s%s%s",
00262 drizzle_result_error_code(result),
00263 drizzle_result_error(result),
00264 table ? ", when using table: " : "", table ? table : "");
00265 drizzle_result_free(result);
00266 }
00267 else
00268 {
00269 fprintf(stdout, "Error: %d, %s%s%s", ret, drizzle_con_error(con),
00270 table ? ", when using table: " : "", table ? table : "");
00271 }
00272
00273 safe_exit(1, con);
00274 }
00275
00276
00277 static char *add_load_option(char *ptr, const char *object,
00278 const char *statement)
00279 {
00280 if (object)
00281 {
00282
00283 if (object[0] == '0' && (object[1] == 'x' || object[1] == 'X'))
00284 ptr+= sprintf(ptr, " %s %s", statement, object);
00285 else
00286 {
00287
00288 ptr+= sprintf(ptr, " %s '", statement);
00289 ptr= field_escape(ptr,object,(uint32_t) strlen(object));
00290 *ptr++= '\'';
00291 }
00292 }
00293 return ptr;
00294 }
00295
00296
00297
00298
00299
00300
00301
00302
00303 static char *field_escape(char *to,const char *from,uint32_t length)
00304 {
00305 const char *end;
00306 uint32_t end_backslashes=0;
00307
00308 for (end= from+length; from != end; from++)
00309 {
00310 *to++= *from;
00311 if (*from == '\\')
00312 end_backslashes^=1;
00313 else
00314 {
00315 if (*from == '\'' && !end_backslashes)
00316 *to++= *from;
00317 end_backslashes=0;
00318 }
00319 }
00320
00321 if (end_backslashes)
00322 *to++= '\\';
00323 return to;
00324 }
00325
00326 void * worker_thread(void *arg)
00327 {
00328 int error;
00329 char *raw_table_name= (char *)arg;
00330 drizzle_con_st *con;
00331
00332 if (!(con= db_connect(current_host,current_db,current_user,opt_password)))
00333 {
00334 return 0;
00335 }
00336
00337
00338
00339
00340 if ((error= write_to_table(raw_table_name, con)))
00341 {
00342 if (exitcode == 0)
00343 {
00344 exitcode= error;
00345 }
00346 }
00347
00348 if (con)
00349 {
00350 db_disconnect(current_host, con);
00351 }
00352
00353 pthread_mutex_lock(&counter_mutex);
00354 counter--;
00355 pthread_cond_signal(&count_threshhold);
00356 pthread_mutex_unlock(&counter_mutex);
00357
00358 return 0;
00359 }
00360
00361
00362 int main(int argc, char **argv)
00363 {
00364 try
00365 {
00366 po::options_description commandline_options("Options used only in command line");
00367 commandline_options.add_options()
00368
00369 ("debug,#", po::value<string>(),
00370 "Output debug log. Often this is 'd:t:o,filename'.")
00371 ("delete,d", po::value<bool>(&opt_delete)->default_value(false)->zero_tokens(),
00372 "First delete all rows from table.")
00373 ("help,?", "Displays this help and exits.")
00374 ("ignore,i", po::value<bool>(&ignore_unique)->default_value(false)->zero_tokens(),
00375 "If duplicate unique key was found, keep old row.")
00376 ("low-priority", po::value<bool>(&opt_low_priority)->default_value(false)->zero_tokens(),
00377 "Use LOW_PRIORITY when updating the table.")
00378 ("replace,r", po::value<bool>(&opt_replace)->default_value(false)->zero_tokens(),
00379 "If duplicate unique key was found, replace old row.")
00380 ("verbose,v", po::value<bool>(&verbose)->default_value(false)->zero_tokens(),
00381 "Print info about the various stages.")
00382 ("version,V", "Output version information and exit.")
00383 ;
00384
00385 po::options_description import_options("Options specific to the drizzleimport");
00386 import_options.add_options()
00387 ("columns,C", po::value<string>(&opt_columns)->default_value(""),
00388 "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.")
00389 ("fields-terminated-by", po::value<string>(&fields_terminated)->default_value(""),
00390 "Fields in the textfile are terminated by ...")
00391 ("fields-enclosed-by", po::value<string>(&enclosed)->default_value(""),
00392 "Fields in the importfile are enclosed by ...")
00393 ("fields-optionally-enclosed-by", po::value<string>(&opt_enclosed)->default_value(""),
00394 "Fields in the i.file are opt. enclosed by ...")
00395 ("fields-escaped-by", po::value<string>(&escaped)->default_value(""),
00396 "Fields in the i.file are escaped by ...")
00397 ("force,f", po::value<bool>(&ignore_errors)->default_value(false)->zero_tokens(),
00398 "Continue even if we get an sql-error.")
00399 ("ignore-lines", po::value<int64_t>(&opt_ignore_lines)->default_value(0),
00400 "Ignore first n lines of data infile.")
00401 ("lines-terminated-by", po::value<string>(&lines_terminated)->default_value(""),
00402 "Lines in the i.file are terminated by ...")
00403 ("local,L", po::value<bool>(&opt_local_file)->default_value(false)->zero_tokens(),
00404 "Read all files through the client.")
00405 ("silent,s", po::value<bool>(&silent)->default_value(false)->zero_tokens(),
00406 "Be more silent.")
00407 ("use-threads", po::value<uint32_t>(&opt_use_threads)->default_value(4),
00408 "Load files in parallel. The argument is the number of threads to use for loading data (default is 4.")
00409 ;
00410
00411 po::options_description client_options("Options specific to the client");
00412 client_options.add_options()
00413 ("host,h", po::value<string>(¤t_host)->default_value("localhost"),
00414 "Connect to host.")
00415 ("password,P", po::value<string>(&password),
00416 "Password to use when connecting to server. If password is not given it's asked from the tty." )
00417 ("port,p", po::value<uint32_t>(&opt_drizzle_port)->default_value(0),
00418 "Port number to use for connection")
00419 ("protocol", po::value<string>(&opt_protocol)->default_value("mysql"),
00420 "The protocol of connection (mysql or drizzle).")
00421 ("user,u", po::value<string>(¤t_user)->default_value(UserDetect().getUser()),
00422 "User for login if not current user.")
00423 ;
00424
00425 po::options_description long_options("Allowed Options");
00426 long_options.add(commandline_options).add(import_options).add(client_options);
00427
00428 std::string system_config_dir_import(SYSCONFDIR);
00429 system_config_dir_import.append("/drizzle/drizzleimport.cnf");
00430
00431 std::string system_config_dir_client(SYSCONFDIR);
00432 system_config_dir_client.append("/drizzle/client.cnf");
00433
00434 std::string user_config_dir((getenv("XDG_CONFIG_HOME")? getenv("XDG_CONFIG_HOME"):"~/.config"));
00435
00436 if (user_config_dir.compare(0, 2, "~/") == 0)
00437 {
00438 char *homedir;
00439 homedir= getenv("HOME");
00440 if (homedir != NULL)
00441 user_config_dir.replace(0, 1, homedir);
00442 }
00443
00444 po::variables_map vm;
00445
00446
00447 int style = po::command_line_style::default_style & ~po::command_line_style::allow_guessing;
00448
00449 po::store(po::command_line_parser(argc, argv).options(long_options).
00450 style(style).extra_parser(parse_password_arg).run(), vm);
00451
00452 std::string user_config_dir_import(user_config_dir);
00453 user_config_dir_import.append("/drizzle/drizzleimport.cnf");
00454
00455 std::string user_config_dir_client(user_config_dir);
00456 user_config_dir_client.append("/drizzle/client.cnf");
00457
00458 ifstream user_import_ifs(user_config_dir_import.c_str());
00459 po::store(parse_config_file(user_import_ifs, import_options), vm);
00460
00461 ifstream user_client_ifs(user_config_dir_client.c_str());
00462 po::store(parse_config_file(user_client_ifs, client_options), vm);
00463
00464 ifstream system_import_ifs(system_config_dir_import.c_str());
00465 store(parse_config_file(system_import_ifs, import_options), vm);
00466
00467 ifstream system_client_ifs(system_config_dir_client.c_str());
00468 po::store(parse_config_file(system_client_ifs, client_options), vm);
00469
00470 po::notify(vm);
00471 if (vm.count("protocol"))
00472 {
00473 boost::to_lower(opt_protocol);
00474 if (not opt_protocol.compare("mysql"))
00475 use_drizzle_protocol=false;
00476 else if (not opt_protocol.compare("drizzle"))
00477 use_drizzle_protocol=true;
00478 else
00479 {
00480 cout << _("Error: Unknown protocol") << " '" << opt_protocol << "'" << endl;
00481 exit(-1);
00482 }
00483 }
00484
00485 if (vm.count("port"))
00486 {
00487
00488
00489
00490
00491 if (opt_drizzle_port > 65535)
00492 {
00493 fprintf(stderr, _("Value supplied for port is not valid.\n"));
00494 exit(EXIT_ARGUMENT_INVALID);
00495 }
00496 }
00497
00498 if( vm.count("password") )
00499 {
00500 if (!opt_password.empty())
00501 opt_password.erase();
00502 if (password == PASSWORD_SENTINEL)
00503 {
00504 opt_password= "";
00505 }
00506 else
00507 {
00508 opt_password= password;
00509 tty_password= false;
00510 }
00511 }
00512 else
00513 {
00514 tty_password= true;
00515 }
00516
00517
00518 if (vm.count("version"))
00519 {
00520 printf("%s Ver %s Distrib %s, for %s-%s (%s)\n", program_name,
00521 IMPORT_VERSION, drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
00522 }
00523
00524 if (vm.count("help") || argc < 2)
00525 {
00526 printf("%s Ver %s Distrib %s, for %s-%s (%s)\n", program_name,
00527 IMPORT_VERSION, drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
00528 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");
00529 printf("\
00530 Loads tables from text files in various formats. The base name of the\n\
00531 text file must be the name of the table that should be used.\n\
00532 If one uses sockets to connect to the Drizzle server, the server will open and\n\
00533 read the text file directly. In other cases the client will open the text\n\
00534 file. The SQL command 'LOAD DATA INFILE' is used to import the rows.\n");
00535
00536 printf("\nUsage: %s [OPTIONS] database textfile...", program_name);
00537 cout<<long_options;
00538 exit(0);
00539 }
00540
00541
00542 if (get_options())
00543 {
00544 return(1);
00545 }
00546
00547 current_db= (*argv)++;
00548 argc--;
00549
00550 if (opt_use_threads)
00551 {
00552 pthread_t mainthread;
00553 pthread_attr_t attr;
00554 pthread_attr_init(&attr);
00555 pthread_attr_setdetachstate(&attr,
00556 PTHREAD_CREATE_DETACHED);
00557
00558 pthread_mutex_init(&counter_mutex, NULL);
00559 pthread_cond_init(&count_threshhold, NULL);
00560
00561 for (counter= 0; *argv != NULL; argv++)
00562 {
00563 pthread_mutex_lock(&counter_mutex);
00564 while (counter == opt_use_threads)
00565 {
00566 struct timespec abstime;
00567
00568 set_timespec(abstime, 3);
00569 pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime);
00570 }
00571
00572 counter++;
00573 pthread_mutex_unlock(&counter_mutex);
00574
00575 if (pthread_create(&mainthread, &attr, worker_thread,
00576 (void *)*argv) != 0)
00577 {
00578 pthread_mutex_lock(&counter_mutex);
00579 counter--;
00580 pthread_mutex_unlock(&counter_mutex);
00581 fprintf(stderr,"%s: Could not create thread\n", program_name);
00582 }
00583 }
00584
00585
00586
00587
00588 pthread_mutex_lock(&counter_mutex);
00589 while (counter)
00590 {
00591 struct timespec abstime;
00592
00593 set_timespec(abstime, 3);
00594 pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime);
00595 }
00596 pthread_mutex_unlock(&counter_mutex);
00597 pthread_mutex_destroy(&counter_mutex);
00598 pthread_cond_destroy(&count_threshhold);
00599 pthread_attr_destroy(&attr);
00600 }
00601 else
00602 {
00603 drizzle_con_st *con;
00604
00605 if (!(con= db_connect(current_host,current_db,current_user,opt_password)))
00606 {
00607 return(1);
00608 }
00609
00610 for (; *argv != NULL; argv++)
00611 if (int error= write_to_table(*argv, con))
00612 if (exitcode == 0)
00613 exitcode= error;
00614 db_disconnect(current_host, con);
00615 }
00616 opt_password.empty();
00617 }
00618 catch(exception &err)
00619 {
00620 cerr<<err.what()<<endl;
00621 }
00622 return(exitcode);
00623 }