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

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