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

Last change on this file since 19177 was 19176, checked in by tbretz, 6 years ago
Fix to the resource description
File size: 31.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 ("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 `/.../.../`")
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 example:\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 --row-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 columns "
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/format/`. Here, the given "
194 "regular expression is applied to the filename and it is newly formated with "
195 "the new format string. Uses the standard formatting rules to replace matches "
196 "(those used by ECMAScript's replace method).\n"
197 "\n"
198 "Usually the previously defined constant values are helpful to create an index "
199 "which relates unambiguously the inserted data to the file. It might be useful "
200 "to delete all data which belongs to this particular file before new data is "
201 "entered. This can be achieved with the `--delete` directive. It deletes all "
202 "data from the table before inserting new data which fulfills the condition "
203 "defined by the `--const` directives.\n"
204 "\n"
205 "The constant values can also be used for a conditional execution (--conditional). "
206 "If any row with the given constant values are found, the execution is stopped "
207 "(note that this happend after the table drop/create but before the delete/insert.\n"
208 "\n"
209 "To ensure efficient access for a conditonal execution, it makes sense to have "
210 "an index created for those columns. This can be done during table creation "
211 "with the --index option.\n"
212 "\n"
213 "If a query failed, the query is printed to stderr together with the error message. "
214 "For the main INSERT query, this is only true if the verbosity level is at least 2 "
215 "or the query has less than 80*25 bytes.\n"
216 "\n"
217 "In case of success, 0 is returned, a value>0 otherwise.\n"
218 "\n"
219 "Usage: root2sql [options] -uri URI rootfile.root\n"
220 "\n"
221 ;
222 cout << endl;
223}
224
225enum BasicType_t
226{
227 kNone = 0,
228 kConst,
229 kFloat,
230 kDouble,
231 kInt16,
232 kUInt16,
233 kInt32,
234 kUInt32,
235 kInt64,
236 kUInt64,
237};
238
239static const map<string, pair<BasicType_t, string>> ConvRoot =
240{
241 { "Float_t", { kFloat, "FLOAT" } },
242 { "Double_t", { kDouble, "DOUBLE" } },
243 { "ULong64_t", { kUInt64, "BIGINT UNSIGNED" } },
244 { "Long64_t", { kInt64, "BIGINT" } },
245 { "UInt_t", { kUInt32, "INT UNSIGNED" } },
246 { "Int_t", { kInt32, "INT" } },
247 { "UShort_t", { kUInt16, "SMALLINT UNSIGNED" } },
248 { "Short_t", { kInt16, "SMALLINT" } },
249};
250
251struct Container
252{
253 static map<void*, size_t> counter;
254
255 string branch; // branch name
256 string column; // column name
257 BasicType_t type;
258 size_t num;
259 void *ptr;
260
261 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)
262 {
263 switch (t)
264 {
265 case kFloat: ptr = new Float_t[n]; break;
266 case kDouble: ptr = new Double_t[n]; break;
267 case kInt16: ptr = new Short_t[n]; break;
268 case kUInt16: ptr = new UShort_t[n]; break;
269 case kInt32: ptr = new Int_t[n]; break;
270 case kUInt32: ptr = new UInt_t[n]; break;
271 case kInt64: ptr = new Long64_t[n]; break;
272 case kUInt64: ptr = new ULong64_t[n]; break;
273 case kConst:
274 case kNone:
275 break;
276 }
277 counter[ptr]++;
278 }
279 Container(const string &c, const string &value) : branch(value), column(c), type(kConst), num(1), ptr(0)
280 {
281 }
282
283 Container(const Container &c) : branch(c.branch), column(c.column), type(c.type), num(c.num), ptr(c.ptr)
284 {
285 counter[ptr]++;
286 }
287
288 ~Container()
289 {
290 counter[ptr]--;
291 if (counter[ptr]==0)
292 ::operator delete[](ptr); // It seems root is deleting it already
293 }
294
295 string fmt(const size_t &index) const
296 {
297 ostringstream str;
298
299 switch (type)
300 {
301 case kFloat: str << setprecision(8) << reinterpret_cast<Float_t*>(ptr)[index]; break;
302 case kDouble: str << setprecision(16) << reinterpret_cast<Double_t*>(ptr)[index]; break;
303 case kInt16: str << reinterpret_cast<Short_t*>(ptr)[index]; break;
304 case kUInt16: str << reinterpret_cast<UShort_t*>(ptr)[index]; break;
305 case kInt32: str << reinterpret_cast<Int_t*>(ptr)[index]; break;
306 case kUInt32: str << reinterpret_cast<UInt_t*>(ptr)[index]; break;
307 case kInt64: str << reinterpret_cast<Long64_t*>(ptr)[index]; break;
308 case kUInt64: str << reinterpret_cast<ULong64_t*>(ptr)[index]; break;
309 case kConst: str << branch; break;
310 case kNone:
311 break;
312 }
313
314 //if (str.str()=="nan" || str.str()=="-nan" || str.str()=="inf" || str.str()=="-inf")
315 // return "NULL";
316
317 return str.str();
318 }
319};
320
321map<void*, size_t> Container::counter;
322
323void ErrorHandlerAll(Int_t level, Bool_t abort, const char *location, const char *msg)
324{
325 if (string(msg).substr(0,24)=="no dictionary for class ")
326 return;
327 if (string(msg).substr(0,15)=="unknown branch ")
328 return;
329
330 DefaultErrorHandler(level, abort, location, msg);
331}
332
333int main(int argc, const char* argv[])
334{
335 Time start;
336
337 gROOT->SetBatch();
338 SetErrorHandler(ErrorHandlerAll);
339
340 Configuration conf(argv[0]);
341 conf.SetPrintUsage(PrintUsage);
342 SetupConfiguration(conf);
343
344 if (!conf.DoParse(argc, argv))
345 return 127;
346
347 // ----------------------------- Evaluate options --------------------------
348 const string uri = conf.Get<string>("uri");
349 const string file = conf.Get<string>("file");
350 const string tree = conf.Get<string>("tree");
351 const string table = conf.Get<string>("table").empty() ? tree : conf.Get<string>("table");
352
353 const uint16_t verbose = conf.Get<uint16_t>("verbose");
354 const int64_t first = conf.Get<int64_t>("first");
355 const int64_t max = conf.Get<int64_t>("max");
356
357 const bool drop = conf.Get<bool>("drop");
358 const bool create = conf.Get<bool>("create") || drop;
359 const bool noinsert = conf.Get<bool>("no-insert");
360 const bool dry_run = conf.Get<bool>("dry-run");
361 const bool conditional = conf.Get<bool>("conditional");
362 const bool run_delete = conf.Get<bool>("delete");
363 const bool index = conf.Get<bool>("index");
364
365 const string engine = conf.Get<string>("engine");
366 const string row_format = conf.Get<string>("row-format");
367
368 const vector<string> duplicate = conf.Vec<string>("duplicate");
369
370 const bool ignore_errors = conf.Get<bool>("ignore-errors");
371
372 const bool print_connection = conf.Get<bool>("print-connection");
373 const bool print_branches = conf.Get<bool>("print-branches");
374 const bool print_leaves = conf.Get<bool>("print-leaves");
375 const bool print_create = conf.Get<bool>("print-create");
376 const bool print_insert = conf.Get<bool>("print-insert");
377 const bool print_select = conf.Get<bool>("print-select");
378 const bool print_delete = conf.Get<bool>("print-delete");
379
380 const vector<Map> mymap = conf.Vec<Map>("map");
381 const vector<Map> sqltypes = conf.Vec<Map>("sql-type");
382 const vector<string> _ignore = conf.Vec<string>("ignore");
383 const vector<string> primary = conf.Vec<string>("primary");
384
385 // -------------------------------------------------------------------------
386
387 if (verbose>0)
388 cout << "\n-------------------------- Evaluating file -------------------------" << endl;
389
390 TFile f(file.c_str());
391 if (f.IsZombie())
392 {
393 cerr << "Could not open file " << file << endl;
394 return 1;
395 }
396
397 if (verbose>0)
398 cout << "File: " << file << endl;
399
400 TTree *T = 0;
401 f.GetObject(tree.c_str(), T);
402 if (!T)
403 {
404 cerr << "Could not open tree " << tree << endl;
405 return 2;
406 }
407
408 if (verbose>0)
409 cout << "Tree: " << tree << endl;
410
411 T->SetMakeClass(1);
412
413 TObjArray *branches = T->GetListOfBranches();
414 TObjArray *leaves = T->GetListOfLeaves();
415
416 if (print_branches)
417 {
418 cout << '\n';
419 branches->Print();
420 }
421
422 if (verbose>0)
423 cout << T->GetEntriesFast() << " events found." << endl;
424
425
426 if (verbose>0)
427 cout << branches->GetEntries() << " branches found." << endl;
428
429 if (print_leaves)
430 {
431 cout << '\n';
432 leaves->Print();
433 }
434 if (verbose>0)
435 cout << leaves->GetEntries() << " leaves found." << endl;
436
437 string query =
438 "CREATE TABLE IF NOT EXISTS `"+table+"`\n"
439 "(\n";
440
441 vector<Container> vec;
442
443 const auto fixed = conf.GetWildcardOptions("const.*");
444
445 string where;
446 vector<string> vindex;
447 for (auto it=fixed.cbegin(); it!=fixed.cend(); it++)
448 {
449 const string name = it->substr(6);
450 string val = conf.Get<string>(*it);
451
452 boost::smatch match;
453 if (boost::regex_match(val, match, boost::regex("\\/(.+)(?<!\\\\)\\/(.*)(?<!\\\\)\\/")))
454 {
455 const string reg = match[1];
456 const string fmt = match[2];
457
458 val = boost::regex_replace(file, boost::regex(reg), fmt.empty()?"$0":fmt,
459 boost::regex_constants::format_default|boost::regex_constants::format_no_copy);
460
461 if (verbose>0)
462 {
463 cout << "Regular expression detected for constant column `" << *it << "`\n";
464 cout << "Filename converted with /" << reg << "/ to /" << fmt << "/\n";
465 cout << "Filename: " << file << '\n';
466 cout << "Result: " << val << endl;
467 }
468 }
469
470 if (verbose>2)
471 cout << "\n" << val << " [-const-]";
472 if (verbose>1)
473 cout << " (" << name << ")";
474
475 string sqltype = "INT UNSIGNED";
476
477 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
478 if (m->first==name)
479 sqltype = m->second;
480
481 if (!vec.empty())
482 query += ",\n";
483 query += " `"+name+"` "+sqltype+" NOT NULL COMMENT '--user--'";
484
485 vec.emplace_back(name, val);
486 where += " AND `"+name+"`="+val;
487 vindex.emplace_back(name);
488 }
489
490 const size_t nvec = vec.size();
491
492 TIter Next(leaves);
493 TObject *o = 0;
494 while ((o=Next()))
495 {
496 TLeaf *L = T->GetLeaf(o->GetName());
497
498 if (verbose>2)
499 cout << '\n' << L->GetTitle() << " {" << L->GetTypeName() << "}";
500
501 if (L->GetLenStatic()!=L->GetLen())
502 {
503 if (verbose>2)
504 cout << " (-skipped-)";
505 continue;
506 }
507
508
509 string name = o->GetName();
510
511 bool found = false;
512 for (auto b=_ignore.cbegin(); b!=_ignore.cend(); b++)
513 {
514 if (boost::regex_match(name, boost::regex(*b)))
515 {
516 found = true;
517 if (verbose>2)
518 cout << " (-ignored-)";
519 break;
520 }
521 }
522 if (found)
523 continue;
524
525 const string tn = L->GetTypeName();
526
527 auto it = ConvRoot.find(tn);
528 if (it==ConvRoot.end())
529 {
530 if (verbose>2)
531 cout << " (-n/a-)";
532 continue;
533 }
534
535 if (verbose==2)
536 cout << '\n' << L->GetTitle() << " {" << L->GetTypeName() << "}";
537
538 for (auto m=mymap.cbegin(); m!=mymap.cend(); m++)
539 name = boost::regex_replace(name, boost::regex(m->first), m->second);
540
541 if (verbose>1)
542 cout << " (" << name << ")";
543
544 string sqltype = it->second.second;
545
546 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
547 if (m->first==name)
548 sqltype = m->second;
549
550 if (!vec.empty())
551 query += ",\n";
552
553 const size_t N = L->GetLenStatic();
554 for (size_t i=0; i<N; i++)
555 {
556 query += " `"+name;
557 if (N>1)
558 query += "["+to_string(i)+"]";
559 query += "` "+sqltype+" NOT NULL COMMENT '"+o->GetTitle()+"'";
560 if (N>1 && i!=N-1)
561 query += ",\n";
562 }
563
564 vec.emplace_back(o->GetTitle(), name, it->second.first, L->GetLenStatic());
565 T->SetBranchAddress(o->GetTitle(), vec.back().ptr);
566 }
567
568 if (verbose>1)
569 cout << "\n\n";
570 if (verbose>0)
571 {
572 if (nvec>0)
573 cout << nvec << " constant value column(s) configured." << endl;
574 cout << vec.size()-nvec << " leaf/leaves setup for reading." << endl;
575 }
576
577 UInt_t datatype = 0;
578 const bool has_datatype = T->SetBranchAddress("DataType.fVal", &datatype) >= 0;
579
580 // Setiing up branch status (must be after all SetBranchAddress)
581 T->SetBranchStatus("*", 0);
582 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
583 if (c->type!=kConst)
584 T->SetBranchStatus(c->branch.c_str(), 1);
585
586 if (has_datatype)
587 {
588 T->SetBranchStatus("DataType.fVal", 1);
589 if (verbose>0)
590 cout << "Rows with DataType.fVal!=1 will be skipped." << endl;
591 }
592
593
594 // -------------------------------------------------------------------------
595 // Checking for database connection
596
597 Database connection(uri);
598
599 if (verbose>0)
600 {
601 cout << "Client Version: " << connection.client_version() << '\n';
602 cout << "Server Version: " << connection.server_version() << endl;
603 }
604
605 if (print_connection)
606 {
607 try
608 {
609 const auto &res1 = connection.query("SHOW STATUS LIKE 'Compression'").store();
610 cout << "Compression of databse connection is " << string(res1[0][1]) << endl;
611
612 const auto &res2 = connection.query("SHOW STATUS LIKE 'Ssl_cipher'").store();
613 cout << "Connection to databases is " << (string(res2[0][1]).empty()?"UNENCRYPTED":"ENCRYPTED ("+string(res2[0][1])+")") << endl;
614 }
615 catch (const exception &e)
616 {
617 cerr << "\nSHOW STATUS LIKE COMPRESSION\n\n";
618 cerr << "SQL query failed:\n" << e.what() << endl;
619 return 3;
620 }
621 }
622
623 // -------------------------------------------------------------------------
624
625 if (verbose>0)
626 cout << "\n--------------------------- Database Table -------------------------" << endl;
627
628 if (!primary.empty())
629 query += ",\n PRIMARY KEY USING BTREE (`"+boost::algorithm::join(primary, "`, `")+"`)";
630
631 if (!vindex.empty() && index)
632 query += ",\n INDEX USING BTREE (`"+boost::algorithm::join(vindex, "`, `")+"`)";
633
634 query +=
635 "\n)\n"
636 "DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci\n";
637 if (!engine.empty())
638 query += "ENGINE="+engine+"\n";
639 if (!row_format.empty())
640 query += "ROW_FORMAT="+row_format+"\n";
641 query += "COMMENT='created by "+conf.GetName()+"'\n";
642
643 // FIXME: Can we omit the catching to be able to print the
644 // query 'autmatically'?
645 try
646 {
647 if (drop)
648 {
649 // => Simple result
650 if (!dry_run)
651 connection.query("DROP TABLE `"+table+"`").execute();
652 if (verbose>0)
653 cout << "Table `" << table << "` dropped." << endl;
654 }
655 }
656 catch (const exception &e)
657 {
658 cerr << "DROP TABLE `" << table << "`\n\n";
659 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
660 return 4;
661 }
662
663 try
664 {
665 if (create && !dry_run)
666 connection.query(query).execute();
667 }
668 catch (const exception &e)
669 {
670 cerr << query << "\n\n";
671 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
672 return 5;
673 }
674
675 if (print_create)
676 cout << query << endl;
677
678 if (create && verbose>0)
679 cout << "Table `" << table << "` created." << endl;
680
681
682 try
683 {
684 if (conditional && !fixed.empty() && !drop)
685 {
686 const mysqlpp::StoreQueryResult res =
687 connection.query("SELECT 1 FROM `"+table+"` WHERE 1"+where+" LIMIT 1").store();
688
689 if (res.num_rows()>0)
690 {
691 if (verbose>0)
692 {
693 cout << "Conditional execution... detected existing rows!\n";
694 cout << "Exit.\n" << endl;
695 }
696 return 0;
697 }
698 }
699 }
700 catch (const exception &e)
701 {
702 cerr << "SELECT 1 FROM `" << table << "` WHERE 1" << where << " LIMIT 1\n\n";
703 cerr << "SQL query failed: " << e.what() << endl;
704 return 6;
705 }
706
707 if (print_select)
708 cout << "SELECT 1 FROM `" << table << "` WHERE 1" << where << " LIMIT 1" << endl;
709
710 try
711 {
712 if (run_delete && !fixed.empty() && !drop && !dry_run)
713 {
714 const mysqlpp::SimpleResult res =
715 connection.query("DELETE FROM `"+table+"` WHERE 1"+where).execute();
716
717 if (verbose>0)
718 cout << res.rows() << " row(s) deleted.\n" << endl;
719 }
720 }
721 catch (const exception &e)
722 {
723 cerr << "DELETE FROM `"+table+"` WHERE 1" << where << "\n\n";
724 cerr << "SQL query failed: " << e.what() << endl;
725 return 7;
726 }
727
728 if (print_delete)
729 cout << "DELETE FROM `"+table+"` WHERE 1" << where << endl;
730
731
732 // -------------------------------------------------------------------------
733
734 if (verbose>0)
735 cout << "\n---------------------------- Reading file --------------------------" << endl;
736
737 //query = update ? "UPDATE" : "INSERT";
738 query = "INSERT ";
739 if (ignore_errors)
740 query += "IGNORE ";
741 query += "`"+table+"`\n"
742 "(\n";
743
744 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
745 {
746 if (c!=vec.cbegin())
747 query += ",\n";
748
749 const size_t N = c->num;
750 for (size_t i=0; i<N; i++)
751 {
752 if (N==1)
753 query += " `"+c->column+"`";
754 else
755 query += " `"+c->column+"["+to_string(i)+"]`";
756
757 if (N>1 && i!=N-1)
758 query += ",\n";
759 }
760 }
761
762 query +=
763 "\n)\n"
764 "VALUES\n";
765
766 size_t count = 0;
767
768 const size_t num = max>0 && (max-first)<T->GetEntriesFast() ? (max-first) : T->GetEntriesFast();
769 for (size_t j=first; j<num; j++)
770 {
771 T->GetEntry(j);
772 if (has_datatype && datatype!=1)
773 continue;
774
775 if (count>0)
776 query += ",\n";
777
778 query += "(\n";
779
780 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
781 {
782 if (c!=vec.cbegin())
783 query += ",\n";
784
785 const size_t N = c->num;
786 for (size_t i=0; i<N; i++)
787 {
788 query += " "+c->fmt(i);
789
790 if (print_insert && i==0)
791 query += " /* "+c->column+" -> "+c->branch+" */";
792
793 if (N>1 && i!=N-1)
794 query += ",\n";
795 }
796 }
797 query += "\n)";
798
799 count ++;
800 }
801
802 if (!duplicate.empty())
803 query += "\nON DUPLICATE KEY UPDATE\n " + boost::join(duplicate, ",\n ");
804
805 if (verbose>0)
806 cout << count << " out of " << num << " row(s) read from file [N=" << first << ".." << num-1 << "]." << endl;
807
808 if (count==0)
809 {
810 if (verbose>0)
811 cout << "Total execution time: " << Time().UnixTime()-start.UnixTime() << "s\n" << endl;
812 return 0;
813 }
814
815 // -------------------------------------------------------------------------
816
817 if (verbose>0)
818 {
819 cout << "\n--------------------------- Inserting data -------------------------" << endl;
820 cout << "Sending INSERT query (" << query.length() << " bytes)" << endl;
821 }
822
823 try
824 {
825 if (!noinsert && !dry_run)
826 {
827 auto q = connection.query(query);
828 q.execute();
829 cout << q.info() << '\n' << endl;
830 }
831 else
832 cout << "Insert query skipped!" << endl;
833
834 if (print_insert)
835 cout << query << endl;
836 }
837 catch (const exception &e)
838 {
839 if (verbose>1 || query.length()<80*25)
840 cerr << query << "\n\n";
841 cerr << "SQL query failed (" << query.length() << " bytes):\n" << e.what() << '\n' << endl;
842 return 8;
843 }
844
845 if (verbose>0)
846 {
847 const auto sec = Time().UnixTime()-start.UnixTime();
848 cout << "Total execution time: " << sec << "s ";
849 cout << "(" << Tools::Fractional(sec/count) << "s/row)\n";
850
851 try
852 {
853 const auto resw =
854 connection.query("SHOW WARNINGS").store();
855
856 for (size_t i=0; i<resw.num_rows(); i++)
857 {
858 const mysqlpp::Row &roww = resw[i];
859
860 cout << roww["Level"] << '[' << roww["Code"] << "]: ";
861 cout << roww["Message"] << '\n';
862 }
863 cout << endl;
864
865 }
866 catch (const exception &e)
867 {
868 cerr << "\nSHOW WARNINGS\n\n";
869 cerr << "SQL query failed:\n" << e.what() << '\n' <<endl;
870 return 9;
871 }
872 }
873
874 if (print_connection)
875 {
876 try
877 {
878 // Exchange _send and _received as it is the view of the server
879 const auto &res1 = connection.query("SHOW STATUS LIKE 'Bytes_%'").store();
880 cout << left << setw(16) << res1[1]["Variable_name"] << ' ' << Tools::Scientific(res1[0]["Value"]) << endl;
881 cout << left << setw(16) << res1[0]["Variable_name"] << ' ' << Tools::Scientific(res1[1]["Value"]) << endl;
882 cout << endl;
883 }
884 catch (const exception &e)
885 {
886 cerr << "\nSHOW STATUS LIKE 'Bytes_%'\n\n";
887 cerr << "SQL query failed:\n" << e.what() << endl;
888 return 10;
889 }
890 }
891
892 return 0;
893}
Note: See TracBrowser for help on using the repository browser.