source: trunk/FACT++/src/root2sql.cc@ 19149

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