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

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