source: trunk/FACT++/src/fits2sql.cc@ 19184

Last change on this file since 19184 was 19181, checked in by tbretz, 7 years ago
Use Boost 1.47 at ISDC, no workaround required anymore.
File size: 31.4 KB
Line 
1#include <boost/algorithm/string/join.hpp>
2#include <boost/regex.hpp>
3
4#include "Database.h"
5
6#include "tools.h"
7#include "Time.h"
8#include "Configuration.h"
9
10#include "zfits.h"
11
12using namespace std;
13
14// ------------------------------------------------------------------------
15
16struct Map : pair<string, string>
17{
18 Map() { }
19};
20
21std::istream &operator>>(std::istream &in, Map &m)
22{
23 const istreambuf_iterator<char> eos;
24 string txt(istreambuf_iterator<char>(in), eos);
25
26 const boost::regex expr("((.*)[^\\\\])/(.*)");
27 boost::smatch match;
28 if (!boost::regex_match(txt, match, expr))
29 throw runtime_error("Could not evaluate map argument: "+txt);
30
31 m.first = match[1].str();
32 m.second = match[3].str();
33
34 return in;
35}
36
37
38void SetupConfiguration(Configuration &conf)
39{
40 po::options_description control("Root to SQL");
41 control.add_options()
42 ("uri,u", var<string>()->required(), "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
43 ("file", var<string>()->required(), "The root file to read from")
44 ("create", po_switch(), "Create the database if not existing")
45 ("drop", po_switch(), "Drop the table (implies create)")
46 ("extension,e", var<string>(""), "Name of the fits extension (table) to convert")
47 ("table", var<string>(""), "Name of the table to use (default is the tree name)")
48 ("map", vars<Map>(), "A regular expression which is applied to the leaf name befoee it is used as SQL column name)")
49 ("sql-type", vars<Map>(), "Allows to overwrite the calculated SQL type for a given column e.g. 'sql-column-name/UNSIGNED IN'")
50 ("ignore", vars<string>(), "Ignore the given leaf, if the given regular expression matches")
51 ("unsigned", vars<string>(), "In fits files per default columns are signed. This interpretss the column as unsigned value. Use the FITS column name.")
52 ("primary", vars<string>(), "List of columns to be used as primary keys during table creation (in connection with --create)")
53 ("first", var<size_t>(size_t(0)), "First event to start with (default: 0), mainly for test purpose")
54 ("max", var<size_t>(size_t(0)), "Maximum number of events to process (0: all), mainly for test purpose")
55 ("engine", var<string>(""), "Database engine to be used when a new table is created")
56 ("row-format", var<string>(""), "Defines the ROW_FORMAT keyword for table creation")
57 ("duplicate", vars<string>(), "Specifies an assignment_list for an 'ON DUPLICATE KEY UPDATE' expression")
58 ("ignore-errors", po_switch(), "Adds the IGNORE keyword to the INSERT query (turns errors into warnings, ignores rows with errors)")
59 ("const.*", var<string>(), "Insert a constant number into the given column (--const.mycolumn=5). A special case is `/.../.../`")
60 ("conditional", po_switch(), "Conditional insert. Only insert if no entry exists yet with the constants defined by --const")
61 ("delete", po_switch(), "Delete all entries first which fit all constant columns defined by --const")
62 ("index", po_switch(), "If a table is created, all const columns are used as a single index")
63 ;
64
65 po::options_description debug("Debug options");
66 debug.add_options()
67 ("no-insert", po_switch(), "Does not insert any data into the table")
68 ("dry-run", po_switch(), "Skip any query which changes the databse (might result in consecutive failures)")
69 ("print-extensions", po_switch(), "Print extensions (tables) from fits file")
70 ("print-connection", po_switch(), "Print database connection information")
71 ("print-columns", po_switch(), "Print columns in fits table")
72 ("print-insert", po_switch(), "Print the INSERT query (note that it contains all data)")
73 ("print-create", po_switch(), "Print the CREATE query")
74 ("print-select", po_switch(), "Print the SELECT query for the conditional execution")
75 ("print-delete", po_switch(), "Print the DELETE query")
76 ("verbose,v", var<uint16_t>(1), "Verbosity (0: quiet, 1: default, 2: more, 3, ...)")
77 ;
78
79 po::positional_options_description p;
80 p.add("file", 1); // The 1st positional options (n=1)
81
82 conf.AddOptions(control);
83 conf.AddOptions(debug);
84 conf.SetArgumentPositions(p);
85}
86
87void PrintUsage()
88{
89 cout <<
90 "fits2sql - Fills the data from a fits file into a database\n"
91 "\n"
92 "For convenience, this documentation uses the extended version of the options, "
93 "refer to the output below to get the abbreviations.\n"
94 "\n"
95 "This is a general purpose tool to fill the contents of a fite file into a database.\n"
96 "\n"
97 "Each fits file contians several table, so called extenbsions. Each tables has "
98 "a number of columns compilsed from basic data types. The default extension to "
99 "read from is the first one on the file but the name can be overwritten using "
100 "--extension. The default table name to fill the data into is identical to "
101 "the extension name. It can be overwritten using --table.\n"
102 "\n"
103 "The name of each column to which data is filled is obtained from "
104 "the fits column names. The column names can be checked using --print-columns. "
105 "Sometimes these names might not be convenient. To allow to simplify or replace "
106 "column names, regular expressions (using boost's regex) can be defined to change "
107 "the names. Note that these regular expressions are applied one by one on each "
108 "columns's name. A valid expression could "
109 "be:\n"
110 " --map=MHillas\\.f/\n"
111 "which would remove all occurances of 'MHillas.f'. This option can be used more than "
112 "once. They are applied in sequence. A single match does not stop the sequence.\n"
113 "\n"
114 "Sometimes it might also be convenient to skip a column. This can be done with "
115 "the --ignore resource. If the given regular expresion yields a match, the "
116 "column will be ignored. Note that the regular expression works on the raw-name "
117 "of the column not the readily mapped SQL column names. Example:\n"
118 " --ignore=ThetaSq\\..*\n"
119 "will skip all leaved which start with 'ThetaSq.'. This option can be used"
120 "more than once.\n"
121 "\n"
122 "The data type of each column is kept as close as possible to the columns' data "
123 "types. If for some reason this is not wanted, the data type of the SQL column "
124 "can be overwritten with --sql-type sql-column/sql-ytpe, for example:\n"
125 " --sql-type=FileId/UNSIGNED INT\n"
126 "while the first argument of the name of the SQL column to which the data type "
127 "should be applied. The second column is the basic SQL data type. The option can "
128 "be given more than once.\n"
129 "\n"
130 "Database interaction:\n"
131 "\n"
132 "To drop an existing table, --drop can be used.\n"
133 "\n"
134 "To create a table according to theSQL column names and data types, --create "
135 "can be used. The query used can be printed with --print-create even --create "
136 "has not been specified.\n"
137 "\n"
138 "To choose the columns which should become primary keys, use --primary, "
139 "for example:\n"
140 " --primary=col1\n"
141 "To define more than one column as primary key, the option can be given more than "
142 "once. Note that the combination of these columns must be unique.\n"
143 "\n"
144 "All columns are created as NOT NULL as default. To force a database engine "
145 "and/or a storage format, use --engine and --rot-format.\n"
146 "\n"
147 "Usually, the INSERT query would fail if the PRIMARY key exists already. "
148 "This can be avoided using the 'ON DUPLICATE KEY UPDATE' directive. With the "
149 "--duplicate, you can specify what should be updated in case of a duplicate key. "
150 "To keep the row untouched, you can just update the primary key "
151 "with the identical primary key, e.g. --duplicate='MyPrimary=VALUES(MyPrimary)'. "
152 "The --duplicate resource can be specified more than once to add more expressions "
153 "to the assignment_list. For more details, see the MySQL manual.\n"
154 "\n"
155 "Another possibility is to add the IGNORE keyword to the INSERT query by "
156 "--ignore-errors, which essentially ignores all errors and turns them into "
157 "warnings which are printed after the query succeeded.\n"
158 "\n"
159 "For debugging purpose, or to just create or drop a table, the final insert "
160 "query can be skipped using --no-insert. Note that for performance reason, "
161 "all data is collected in memory and a single INSERT query is issued at the "
162 "end.\n"
163 "\n"
164 "Using a higher verbosity level (-v), an overview of the written columns or all "
165 "processed leaves is printed depending on the verbosity level. The output looks "
166 "like the following\n"
167 " Leaf name [root data type] (SQL name)\n"
168 "for example\n"
169 " MTime.fTime.fMilliSec [Long64_t] (MilliSec)\n"
170 "which means that the leaf MTime.fTime.fMilliSec is detected to be a Long64_t "
171 "which is filled into a column called MilliSec. Leaves with non basic data types "
172 "are ignored automatically and are marked as (-n/a-). User ignored columnd "
173 "are marked as (-ignored-).\n"
174 "\n"
175 "A constant value for the given file can be inserted by using the --const directive. "
176 "For example --const.mycolumn=42 would insert 42 into a column called mycolumn. "
177 "The column is created as INT UNSIGNED as default which can be altered by "
178 "--sql-type. A special case is a value of the form `/regex/format/`. Here, the given "
179 "regular expression is applied to the filename and it is newly formated with "
180 "the new format string. For details on how backreferences work, see for example "
181 "the man-page of the sed utility.\n"
182 "\n"
183 "Usually the previously defined constant values are helpful to create an index "
184 "which relates unambiguously the inserted data to the file. It might be useful "
185 "to delete all data which belongs to this particular file before new data is "
186 "entered. This can be achieved with the `--delete` directive. It deletes all "
187 "data from the table before inserting new data which fulfills the condition "
188 "defined by the `--const` directives.\n"
189 "\n"
190 "The constant values can also be used for a conditional execution (--conditional). "
191 "If any row with the given constant values are found, the execution is stopped "
192 "(note that this happend after the table drop/create but before the delete/insert.\n"
193 "\n"
194 "To ensure efficient access for a conditonal execution, it makes sense to have "
195 "an index created for those columns. This can be done during table creation "
196 "with the --index option.\n"
197 "\n"
198 "If a query failed, the query is printed to stderr together with the error message. "
199 "For the main INSERT query, this is only true if the verbosity level is at least 2 "
200 "or the query has less than 80*25 bytes.\n"
201 "\n"
202 "In case of success, 0 is returned, a value>0 otherwise.\n"
203 "\n"
204 "Usage: fits2sql [options] --uri URI fitsfile.fits[.gz]\n"
205 "\n"
206 ;
207 cout << endl;
208}
209
210enum BasicType_t
211{
212 kNone = 0,
213 kConst,
214 kVarchar,
215 kBool,
216 kFloat,
217 kDouble,
218 kInt8,
219 kUInt8,
220 kInt16,
221 kUInt16,
222 kInt32,
223 kUInt32,
224 kInt64,
225 kUInt64,
226// kMJD,
227};
228
229static const map<char, pair<BasicType_t, string>> ConvFits =
230{
231 { 'A', { kVarchar, "VARCHAR" } },
232 { 'a', { kVarchar, "VARCHAR" } },
233 { 'L', { kBool, "BOOLEAN" } },
234 { 'l', { kBool, "BOOLEAN" } },
235 { 'B', { kInt8, "TINYINT" } },
236 { 'b', { kUInt8, "TINYINT UNSIGNED" } },
237 { 'I', { kInt16, "SMALLINT" } },
238 { 'i', { kUInt16, "SMALLINT UNSIGNED" } },
239 { 'J', { kInt32, "INT" } },
240 { 'j', { kUInt32, "INT UNSIGNED" } },
241 { 'K', { kInt64, "BIGINT" } },
242 { 'k', { kUInt64, "BIGINT UNSIGNED" } },
243 { 'E', { kFloat, "FLOAT" } },
244 { 'e', { kFloat, "FLOAT" } },
245 { 'D', { kDouble, "DOUBLE" } },
246 { 'd', { kDouble, "DOUBLE" } },
247};
248
249struct Container
250{
251 string branch; // fits column name
252 string column; // sql column name
253 BasicType_t type;
254 size_t num;
255// double offset;
256 void *ptr;
257
258 Container(const string &b, const string &c, const BasicType_t &t, const size_t &n/*, const double &offset=0*/) : branch(b), column(c), type(t), num(n), ptr(0)
259 {
260 }
261 Container(const string &c, const string &value) : branch(value), column(c), type(kConst), num(1), ptr(0)
262 {
263 }
264 ~Container()
265 {
266 }
267
268 string fmt(const size_t &index) const
269 {
270 ostringstream str;
271
272 switch (type)
273 {
274 //case kVarchar: str << string(reinterpret_cast<char*>(ptr), num); break;
275 case kVarchar: str << string(reinterpret_cast<char*>(ptr), num).c_str(); break;
276 case kFloat: str << setprecision(8) << reinterpret_cast<float*>(ptr)[index]; break;
277// case kMJD: str << setprecision(16) << reinterpret_cast<double*>(ptr)[0]+offset; break;
278 case kDouble: str << setprecision(16) << reinterpret_cast<double*>(ptr)[index]; break;
279 case kBool:
280 case kInt8: str << int32_t(reinterpret_cast<int8_t*>(ptr)[index]); break;
281 case kUInt8: str << uint32_t(reinterpret_cast<uint8_t*>(ptr)[index]); break;
282 case kInt16: str << reinterpret_cast<int16_t*>(ptr)[index]; break;
283 case kUInt16: str << reinterpret_cast<uint16_t*>(ptr)[index]; break;
284 case kInt32: str << reinterpret_cast<int32_t*>(ptr)[index]; break;
285 case kUInt32: str << reinterpret_cast<uint32_t*>(ptr)[index]; break;
286 case kInt64: str << reinterpret_cast<int64_t*>(ptr)[index]; break;
287 case kUInt64: str << reinterpret_cast<uint64_t*>(ptr)[index]; break;
288 case kConst: str << branch; break;
289 case kNone:
290 break;
291 }
292
293 return str.str();
294 }
295};
296
297int main(int argc, const char* argv[])
298{
299 Time start;
300
301 Configuration conf(argv[0]);
302 conf.SetPrintUsage(PrintUsage);
303 SetupConfiguration(conf);
304
305 if (!conf.DoParse(argc, argv))
306 return 127;
307
308 // ----------------------------- Evaluate options --------------------------
309 const string uri = conf.Get<string>("uri");
310 const string file = conf.Get<string>("file");
311 const string extension = conf.Get<string>("extension");
312 string table = conf.Get<string>("table");
313
314 const uint16_t verbose = conf.Get<uint16_t>("verbose");
315 const size_t first = conf.Get<size_t>("first");
316 const size_t max = conf.Get<size_t>("max");
317
318 const bool drop = conf.Get<bool>("drop");
319 const bool create = conf.Get<bool>("create") || drop;
320 const bool noinsert = conf.Get<bool>("no-insert");
321 const bool dry_run = conf.Get<bool>("dry-run");
322 const bool conditional = conf.Get<bool>("conditional");
323 const bool run_delete = conf.Get<bool>("delete");
324 const bool index = conf.Get<bool>("index");
325
326 const string engine = conf.Get<string>("engine");
327 const string row_format = conf.Get<string>("row-format");
328
329 const bool ignore_errors = conf.Get<bool>("ignore-errors");
330
331 const bool print_connection = conf.Get<bool>("print-connection");
332 const bool print_extensions = conf.Get<bool>("print-extensions");
333 const bool print_columns = conf.Get<bool>("print-columns");
334 const bool print_create = conf.Get<bool>("print-create");
335 const bool print_insert = conf.Get<bool>("print-insert");
336 const bool print_select = conf.Get<bool>("print-select");
337 const bool print_delete = conf.Get<bool>("print-delete");
338
339 const vector<Map> mymap = conf.Vec<Map>("map");
340 const vector<Map> sqltypes = conf.Vec<Map>("sql-type");
341 const vector<string> _ignore = conf.Vec<string>("ignore");
342 const vector<string> primary = conf.Vec<string>("primary");
343
344 const vector<string> duplicate = conf.Vec<string>("duplicate");
345 const vector<string> notsigned = conf.Vec<string>("unsigned");
346
347 if (first>=max)
348 cerr << "WARNING: Resource `first` (" << first << ") exceeds `max` (" << max << ")" << endl;
349
350
351 // -------------------------------------------------------------------------
352
353 if (verbose>0)
354 {
355 cout << "\n-------------------------- Evaluating file -------------------------" << endl;
356 cout << "Start Time: " << Time::sql << Time(Time::local) << endl;
357 }
358
359 zfits f(file.c_str(), extension.c_str());
360 if (!f)
361 {
362 cerr << "Could not open file " << file << ": " << strerror(errno) << endl;
363 return 1;
364 }
365
366 if (verbose>0)
367 cout << "File: " << file << endl;
368
369 if (!extension.empty() && extension!=f.Get<string>("EXTNAME"))
370 {
371 cerr << "Extension " << extension << " not found in file." << endl;
372 return 2;
373 }
374
375 if (print_extensions)
376 {
377 cout << "\nTables:\n - " << boost::join(f.GetTables(), "\n - ") << '\n' << endl;
378 return 3;
379 }
380
381 if (verbose>0)
382 cout << "FITS extension [table]: " << f.Get<string>("EXTNAME") << endl;
383
384 if (table.empty())
385 table = f.Get<string>("EXTNAME");
386
387 if (verbose>0)
388 cout << "SQL table: " << table << endl;
389
390// const double mjdref = f.Get("MJDREF", double(0));
391
392 if (print_columns)
393 {
394 cout << '\n';
395 f.PrintColumns();
396 cout << '\n';
397 }
398
399 const auto cols = f.GetColumns();
400
401 if (verbose>0)
402 {
403 cout << f.GetNumRows() << " events found." << endl;
404 cout << cols.size() << " columns found." << endl;
405 }
406
407 string query =
408 "CREATE TABLE IF NOT EXISTS `"+table+"`\n"
409 "(\n";
410
411 vector<Container> vec;
412
413 const auto fixed = conf.GetWildcardOptions("const.*");
414
415 string where;
416 vector<string> vindex;
417 for (auto it=fixed.cbegin(); it!=fixed.cend(); it++)
418 {
419 const string name = it->substr(6);
420 string val = conf.Get<string>(*it);
421
422 boost::smatch match;
423 if (boost::regex_match(val, match, boost::regex("\\/(.+)(?<!\\\\)\\/(.*)(?<!\\\\)\\/")))
424 {
425 const string reg = match[1];
426 const string fmt = match[2];
427
428 val = boost::regex_replace(file, boost::regex(reg), fmt.empty()?"$0":fmt,
429 boost::regex_constants::format_default|boost::regex_constants::format_no_copy);
430
431 if (verbose>0)
432 {
433 cout << "Regular expression detected for constant column `" << *it << "`\n";
434 cout << "Filename converted with /" << reg << "/ to /" << fmt << "/\n";
435 cout << "Filename: " << file << '\n';
436 cout << "Result: " << val << endl;
437 }
438 }
439
440 if (verbose==2)
441 cout << "\n" << val << " [-const-]";
442 if (verbose>1)
443 cout << " (" << name << ")";
444
445 string sqltype = "INT UNSIGNED";
446
447 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
448 if (m->first==name)
449 sqltype = m->second;
450
451 if (!vec.empty())
452 query += ",\n";
453 query += " `"+name+"` "+sqltype+" NOT NULL COMMENT '--user--'";
454
455 vec.emplace_back(name, val);
456 where += " AND `"+name+"`="+val;
457 vindex.emplace_back(name);
458 }
459
460 const size_t nvec = vec.size();
461
462 for (auto ic=cols.cbegin(); ic!=cols.cend(); ic++)
463 {
464 const auto &col = ic->second;
465
466 if (verbose>2)
467 cout << '\n' << col.type << " " << ic->first << "[" << col.num << "]";
468
469 string name = ic->first;
470
471 bool found = false;
472 for (auto b=_ignore.cbegin(); b!=_ignore.cend(); b++)
473 {
474 if (boost::regex_match(name, boost::regex(*b)))
475 {
476 found = true;
477 if (verbose>2)
478 cout << " (-ignored-)";
479 break;
480 }
481 }
482 if (found)
483 continue;
484
485 const char tn = find(notsigned.cbegin(), notsigned.cend(), ic->first)!=notsigned.cend() ?
486 tolower(col.type) : toupper(col.type);
487
488 auto it = ConvFits.find(tn);
489 if (it==ConvFits.end())
490 {
491 if (verbose>2)
492 cout << " (-n/a-)";
493 continue;
494 }
495
496 if (verbose==2)
497 cout << '\n' << name << " [" << tn << "]";
498
499 for (auto m=mymap.cbegin(); m!=mymap.cend(); m++)
500 name = boost::regex_replace(name, boost::regex(m->first), m->second);
501
502 if (verbose>1)
503 cout << " (" << name << ")";
504
505 string sqltype = it->second.second;
506
507 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
508 if (m->first==name)
509 sqltype = m->second;
510
511 if (!vec.empty())
512 query += ",\n";
513
514 const size_t N = col.type=='A' ? 1 : col.num;
515 for (size_t i=0; i<N; i++)
516 {
517 query += " `"+name;
518 if (N>1)
519 query += "["+to_string(i)+"]";
520 query += "` "+sqltype;
521 if (col.type=='A')
522 query += '('+to_string(col.num)+')';
523 query += " NOT NULL COMMENT '"+ic->first;
524 if (!col.unit.empty())
525 query += "["+col.unit+"]";
526 if (!col.comment.empty())
527 query += ": "+col.comment;
528 query += +"'";
529 if (N>1 && i!=N-1)
530 query += ",\n";
531 }
532
533 const BasicType_t bt =
534 /*ic.first=="Time" && col.num==1 && col.unit=="MJD" && it->second.first==kDouble ?
535 kMJD :*/ it->second.first;
536
537 vec.emplace_back(ic->first, name, bt, col.num/*, mjdref*/);
538 vec.back().ptr = f.SetPtrAddress(ic->first);
539 }
540
541 if (verbose>1)
542 cout << "\n\n";
543 if (verbose>0)
544 {
545 if (nvec>0)
546 cout << nvec << " constant value column(s) configured." << endl;
547 cout << vec.size()-nvec << " FITS columns setup for reading." << endl;
548 }
549
550 // -------------------------------------------------------------------------
551 // Checking for database connection
552 if (verbose>0)
553 cout << "\n---------------------- Connecting database -------------------------" << endl;
554
555 if (verbose>0)
556 {
557 cout << "Connecting to database...\n";
558 cout << "Client Version: " << mysqlpp::Connection().client_version() << endl;
559 }
560
561 Database connection(uri); // Keep alive while fetching rows
562
563 if (verbose>0)
564 cout << "Server Version: " << connection.server_version() << endl;
565
566 if (print_connection)
567 {
568 try
569 {
570 const auto &res1 = connection.query("SHOW STATUS LIKE 'Compression'").store();
571 cout << "Compression of databse connection is " << string(res1[0][1]) << endl;
572
573 const auto &res2 = connection.query("SHOW STATUS LIKE 'Ssl_cipher'").store();
574 cout << "Connection to databases is " << (string(res2[0][1]).empty()?"UNENCRYPTED":"ENCRYPTED ("+string(res2[0][1])+")") << endl;
575 }
576 catch (const exception &e)
577 {
578 cerr << "\nSHOW STATUS LIKE COMPRESSION\n\n";
579 cerr << "SQL query failed:\n" << e.what() << endl;
580 return 4;
581 }
582 }
583
584 // -------------------------------------------------------------------------
585
586 if (verbose>0)
587 cout << "\n--------------------------- Database Table -------------------------" << endl;
588
589 if (!primary.empty())
590 query += ",\n PRIMARY KEY USING BTREE (`"+boost::algorithm::join(primary, "`, `")+"`)";
591
592 if (!vindex.empty() && index)
593 query += ",\n INDEX USING BTREE (`"+boost::algorithm::join(vindex, "`, `")+"`)";
594
595 query +=
596 "\n)\n"
597 "DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci\n";
598 if (!engine.empty())
599 query += "ENGINE="+engine+"\n";
600 if (!row_format.empty())
601 query += "ROW_FORMAT="+row_format+"\n";
602 query += "COMMENT='created by "+conf.GetName()+"'\n";
603
604
605 // FIXME: Can we omit the catching to be able to print the
606 // query 'autmatically'?
607 try
608 {
609 if (drop)
610 {
611 // => Simple result
612 if (!dry_run)
613 connection.query("DROP TABLE `"+table+"`").execute();
614 if (verbose>0)
615 cout << "Table `" << table << "` dropped." << endl;
616 }
617 }
618 catch (const exception &e)
619 {
620 cerr << "DROP TABLE `" << table << "`\n\n";
621 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
622 return 5;
623 }
624
625 try
626 {
627 if (create && !dry_run)
628 connection.query(query).execute();
629 }
630 catch (const exception &e)
631 {
632 cerr << query << "\n\n";
633 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
634 return 6;
635 }
636
637 if (print_create)
638 cout << query << endl;
639
640 if (create && verbose>0)
641 cout << "Table `" << table << "` created." << endl;
642
643
644 try
645 {
646 if (conditional && !fixed.empty() && !drop)
647 {
648 const mysqlpp::StoreQueryResult res =
649 connection.query("SELECT 1 FROM `"+table+"` WHERE 1"+where+" LIMIT 1").store();
650
651 if (print_select)
652 cout << "SELECT 1 FROM `"+table+"` WHERE 1" << where << " LIMIT 1" << endl;
653
654 if (res.num_rows()>0)
655 {
656 if (verbose>0)
657 {
658 cout << "Conditional execution... detected existing rows!\n";
659 cout << "Exit.\n" << endl;
660 }
661 return 0;
662 }
663 }
664 }
665 catch (const exception &e)
666 {
667 cerr << "SELECT 1 FROM `"+table+"` WHERE 1" << where << " LIMIT 1\n\n";
668 cerr << "SQL query failed: " << e.what() << endl;
669 return 7;
670 }
671
672 try
673 {
674 if (run_delete && !fixed.empty() && !drop && !dry_run)
675 {
676 const mysqlpp::SimpleResult res =
677 connection.query("DELETE FROM `"+table+"` WHERE 1"+where).execute();
678
679 if (verbose>0)
680 cout << res.rows() << " row(s) deleted.\n" << endl;
681 }
682 }
683 catch (const exception &e)
684 {
685 cerr << "DELETE FROM `" << table << "` WHERE 1" << where << "\n\n";
686 cerr << "SQL query failed: " << e.what() << endl;
687 return 8;
688 }
689
690 if (print_delete)
691 cout << "DELETE FROM `" << table << "` WHERE 1" << where << endl;
692
693 // -------------------------------------------------------------------------
694
695 if (verbose>0)
696 cout << "\n---------------------------- Reading file --------------------------" << endl;
697
698 //query = update ? "UPDATE" : "INSERT";
699 query = "INSERT ";
700 if (ignore_errors)
701 query += "IGNORE ";
702 query += "`"+table+"`\n"
703 "(\n";
704
705 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
706 {
707 if (c!=vec.cbegin())
708 query += ",\n";
709
710 const size_t N = c->type==kVarchar ? 1 : c->num;
711 for (size_t i=0; i<N; i++)
712 {
713 if (N==1)
714 query += " `"+c->column+"`";
715 else
716 query += " `"+c->column+"["+to_string(i)+"]`";
717
718 if (N>1 && i!=N-1)
719 query += ",\n";
720 }
721 }
722
723 query +=
724 "\n)\n"
725 "VALUES\n";
726
727 size_t count = 0;
728
729 const size_t num = max>first && (max-first)<f.GetNumRows() ? (max-first) : f.GetNumRows();
730 for (size_t j=first; j<num; j++)
731 {
732 f.GetRow(j);
733
734 if (count>0)
735 query += ",\n";
736
737 query += "(\n";
738
739 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
740 {
741 if (c!=vec.cbegin())
742 query += ",\n";
743
744 const size_t N = c->type==kVarchar ? 1 : c->num;
745 for (size_t i=0; i<N; i++)
746 {
747 if (c->type==kVarchar)
748 query += " '"+c->fmt(i)+"'";
749 else
750 query += " "+c->fmt(i);
751
752 if (print_insert && i==0)
753 query += " /* "+c->column+" -> "+c->branch+" */";
754
755 if (N>1 && i!=N-1)
756 query += ",\n";
757 }
758 }
759 query += "\n)";
760
761 count ++;
762 }
763
764 if (!duplicate.empty())
765 query += "\nON DUPLICATE KEY UPDATE\n " + boost::join(duplicate, ",\n ");
766
767 if (verbose>0)
768 cout << count << " out of " << num << " row(s) read from file [N=" << first << ".." << num-1 << "]." << endl;
769
770 if (count==0)
771 {
772 if (verbose>0)
773 cout << "Total execution time: " << Time().UnixTime()-start.UnixTime() << "s\n" << endl;
774 return 0;
775 }
776
777 // -------------------------------------------------------------------------
778
779 if (verbose>0)
780 {
781 cout << "\n--------------------------- Inserting data -------------------------" << endl;
782 cout << "Sending INSERT query (" << query.length() << " bytes)" << endl;
783 }
784
785 try
786 {
787 if (!noinsert && !dry_run)
788 {
789 auto q = connection.query(query);
790 q.execute();
791 cout << q.info() << '\n' << endl;
792 }
793 else
794 cout << "Insert query skipped!" << endl;
795
796 if (print_insert)
797 cout << query << endl;
798 }
799 catch (const exception &e)
800 {
801 if (verbose>1 || query.length()<80*25)
802 cerr << query << "\n\n";
803 cerr << "SQL query failed (" << query.length() << " bytes):\n" << e.what() << '\n' << endl;
804 return 9;
805 }
806
807 if (verbose>0)
808 {
809 const auto sec = Time().UnixTime()-start.UnixTime();
810 cout << "Total execution time: " << sec << "s ";
811 cout << "(" << Tools::Fractional(sec/count) << "s/row)\n";
812
813 try
814 {
815 const auto resw =
816 connection.query("SHOW WARNINGS").store();
817
818 for (size_t i=0; i<resw.num_rows(); i++)
819 {
820 const mysqlpp::Row &roww = resw[i];
821
822 cout << roww["Level"] << '[' << roww["Code"] << "]: ";
823 cout << roww["Message"] << '\n';
824 }
825 cout << endl;
826
827 }
828 catch (const exception &e)
829 {
830 cerr << "\nSHOW WARNINGS\n\n";
831 cerr << "SQL query failed:\n" << e.what() << '\n' <<endl;
832 return 10;
833 }
834 }
835
836 if (print_connection)
837 {
838 try
839 {
840 // Exchange _send and _received as it is the view of the server
841 const auto &res1 = connection.query("SHOW STATUS LIKE 'Bytes_%'").store();
842 cout << left << setw(16) << res1[1]["Variable_name"] << ' ' << Tools::Scientific(res1[0]["Value"]) << endl;
843 cout << left << setw(16) << res1[0]["Variable_name"] << ' ' << Tools::Scientific(res1[1]["Value"]) << endl;
844 cout << endl;
845 }
846 catch (const exception &e)
847 {
848 cerr << "\nSHOW STATUS LIKE 'Bytes_%'\n\n";
849 cerr << "SQL query failed:\n" << e.what() << endl;
850 return 11;
851 }
852 }
853
854 return 0;
855}
Note: See TracBrowser for help on using the repository browser.