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

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