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

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