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

Last change on this file since 19178 was 19178, checked in by tbretz, 6 years ago
Print start time.
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>()
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 {
389 cout << "\n-------------------------- Evaluating file -------------------------\n";
390 cout << "Start Time: " << Time::sql << Time(Time::local) << endl;
391 }
392
393 TFile f(file.c_str());
394 if (f.IsZombie())
395 {
396 cerr << "Could not open file " << file << endl;
397 return 1;
398 }
399
400 if (verbose>0)
401 cout << "File: " << file << endl;
402
403 TTree *T = 0;
404 f.GetObject(tree.c_str(), T);
405 if (!T)
406 {
407 cerr << "Could not open tree " << tree << endl;
408 return 2;
409 }
410
411 if (verbose>0)
412 cout << "Tree: " << tree << endl;
413
414 T->SetMakeClass(1);
415
416 TObjArray *branches = T->GetListOfBranches();
417 TObjArray *leaves = T->GetListOfLeaves();
418
419 if (print_branches)
420 {
421 cout << '\n';
422 branches->Print();
423 }
424
425 if (verbose>0)
426 cout << T->GetEntriesFast() << " events found." << endl;
427
428
429 if (verbose>0)
430 cout << branches->GetEntries() << " branches found." << endl;
431
432 if (print_leaves)
433 {
434 cout << '\n';
435 leaves->Print();
436 }
437 if (verbose>0)
438 cout << leaves->GetEntries() << " leaves found." << endl;
439
440 string query =
441 "CREATE TABLE IF NOT EXISTS `"+table+"`\n"
442 "(\n";
443
444 vector<Container> vec;
445
446 const auto fixed = conf.GetWildcardOptions("const.*");
447
448 string where;
449 vector<string> vindex;
450 for (auto it=fixed.cbegin(); it!=fixed.cend(); it++)
451 {
452 const string name = it->substr(6);
453 string val = conf.Get<string>(*it);
454
455 boost::smatch match;
456 if (boost::regex_match(val, match, boost::regex("\\/(.+)(?<!\\\\)\\/(.*)(?<!\\\\)\\/")))
457 {
458 const string reg = match[1];
459 const string fmt = match[2];
460
461 val = boost::regex_replace(file, boost::regex(reg), fmt.empty()?"$0":fmt,
462 boost::regex_constants::format_default|boost::regex_constants::format_no_copy);
463
464 if (verbose>0)
465 {
466 cout << "Regular expression detected for constant column `" << *it << "`\n";
467 cout << "Filename converted with /" << reg << "/ to /" << fmt << "/\n";
468 cout << "Filename: " << file << '\n';
469 cout << "Result: " << val << endl;
470 }
471 }
472
473 if (verbose>2)
474 cout << "\n" << val << " [-const-]";
475 if (verbose>1)
476 cout << " (" << name << ")";
477
478 string sqltype = "INT UNSIGNED";
479
480 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
481 if (m->first==name)
482 sqltype = m->second;
483
484 if (!vec.empty())
485 query += ",\n";
486 query += " `"+name+"` "+sqltype+" NOT NULL COMMENT '--user--'";
487
488 vec.emplace_back(name, val);
489 where += " AND `"+name+"`="+val;
490 vindex.emplace_back(name);
491 }
492
493 const size_t nvec = vec.size();
494
495 TIter Next(leaves);
496 TObject *o = 0;
497 while ((o=Next()))
498 {
499 TLeaf *L = T->GetLeaf(o->GetName());
500
501 if (verbose>2)
502 cout << '\n' << L->GetTitle() << " {" << L->GetTypeName() << "}";
503
504 if (L->GetLenStatic()!=L->GetLen())
505 {
506 if (verbose>2)
507 cout << " (-skipped-)";
508 continue;
509 }
510
511
512 string name = o->GetName();
513
514 bool found = false;
515 for (auto b=_ignore.cbegin(); b!=_ignore.cend(); b++)
516 {
517 if (boost::regex_match(name, boost::regex(*b)))
518 {
519 found = true;
520 if (verbose>2)
521 cout << " (-ignored-)";
522 break;
523 }
524 }
525 if (found)
526 continue;
527
528 const string tn = L->GetTypeName();
529
530 auto it = ConvRoot.find(tn);
531 if (it==ConvRoot.end())
532 {
533 if (verbose>2)
534 cout << " (-n/a-)";
535 continue;
536 }
537
538 if (verbose==2)
539 cout << '\n' << L->GetTitle() << " {" << L->GetTypeName() << "}";
540
541 for (auto m=mymap.cbegin(); m!=mymap.cend(); m++)
542 name = boost::regex_replace(name, boost::regex(m->first), m->second);
543
544 if (verbose>1)
545 cout << " (" << name << ")";
546
547 string sqltype = it->second.second;
548
549 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
550 if (m->first==name)
551 sqltype = m->second;
552
553 if (!vec.empty())
554 query += ",\n";
555
556 const size_t N = L->GetLenStatic();
557 for (size_t i=0; i<N; i++)
558 {
559 query += " `"+name;
560 if (N>1)
561 query += "["+to_string(i)+"]";
562 query += "` "+sqltype+" NOT NULL COMMENT '"+o->GetTitle()+"'";
563 if (N>1 && i!=N-1)
564 query += ",\n";
565 }
566
567 vec.emplace_back(o->GetTitle(), name, it->second.first, L->GetLenStatic());
568 T->SetBranchAddress(o->GetTitle(), vec.back().ptr);
569 }
570
571 if (verbose>1)
572 cout << "\n\n";
573 if (verbose>0)
574 {
575 if (nvec>0)
576 cout << nvec << " constant value column(s) configured." << endl;
577 cout << vec.size()-nvec << " leaf/leaves setup for reading." << endl;
578 }
579
580 UInt_t datatype = 0;
581 const bool has_datatype = T->SetBranchAddress("DataType.fVal", &datatype) >= 0;
582
583 // Setiing up branch status (must be after all SetBranchAddress)
584 T->SetBranchStatus("*", 0);
585 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
586 if (c->type!=kConst)
587 T->SetBranchStatus(c->branch.c_str(), 1);
588
589 if (has_datatype)
590 {
591 T->SetBranchStatus("DataType.fVal", 1);
592 if (verbose>0)
593 cout << "Rows with DataType.fVal!=1 will be skipped." << endl;
594 }
595
596
597 // -------------------------------------------------------------------------
598 // Checking for database connection
599
600 Database connection(uri);
601
602 if (verbose>0)
603 {
604 cout << "Client Version: " << connection.client_version() << '\n';
605 cout << "Server Version: " << connection.server_version() << endl;
606 }
607
608 if (print_connection)
609 {
610 try
611 {
612 const auto &res1 = connection.query("SHOW STATUS LIKE 'Compression'").store();
613 cout << "Compression of databse connection is " << string(res1[0][1]) << endl;
614
615 const auto &res2 = connection.query("SHOW STATUS LIKE 'Ssl_cipher'").store();
616 cout << "Connection to databases is " << (string(res2[0][1]).empty()?"UNENCRYPTED":"ENCRYPTED ("+string(res2[0][1])+")") << endl;
617 }
618 catch (const exception &e)
619 {
620 cerr << "\nSHOW STATUS LIKE COMPRESSION\n\n";
621 cerr << "SQL query failed:\n" << e.what() << endl;
622 return 3;
623 }
624 }
625
626 // -------------------------------------------------------------------------
627
628 if (verbose>0)
629 cout << "\n--------------------------- Database Table -------------------------" << endl;
630
631 if (!primary.empty())
632 query += ",\n PRIMARY KEY USING BTREE (`"+boost::algorithm::join(primary, "`, `")+"`)";
633
634 if (!vindex.empty() && index)
635 query += ",\n INDEX USING BTREE (`"+boost::algorithm::join(vindex, "`, `")+"`)";
636
637 query +=
638 "\n)\n"
639 "DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci\n";
640 if (!engine.empty())
641 query += "ENGINE="+engine+"\n";
642 if (!row_format.empty())
643 query += "ROW_FORMAT="+row_format+"\n";
644 query += "COMMENT='created by "+conf.GetName()+"'\n";
645
646 // FIXME: Can we omit the catching to be able to print the
647 // query 'autmatically'?
648 try
649 {
650 if (drop)
651 {
652 // => Simple result
653 if (!dry_run)
654 connection.query("DROP TABLE `"+table+"`").execute();
655 if (verbose>0)
656 cout << "Table `" << table << "` dropped." << endl;
657 }
658 }
659 catch (const exception &e)
660 {
661 cerr << "DROP TABLE `" << table << "`\n\n";
662 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
663 return 4;
664 }
665
666 try
667 {
668 if (create && !dry_run)
669 connection.query(query).execute();
670 }
671 catch (const exception &e)
672 {
673 cerr << query << "\n\n";
674 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
675 return 5;
676 }
677
678 if (print_create)
679 cout << query << endl;
680
681 if (create && verbose>0)
682 cout << "Table `" << table << "` created." << endl;
683
684
685 try
686 {
687 if (conditional && !fixed.empty() && !drop)
688 {
689 const mysqlpp::StoreQueryResult res =
690 connection.query("SELECT 1 FROM `"+table+"` WHERE 1"+where+" LIMIT 1").store();
691
692 if (res.num_rows()>0)
693 {
694 if (verbose>0)
695 {
696 cout << "Conditional execution... detected existing rows!\n";
697 cout << "Exit.\n" << endl;
698 }
699 return 0;
700 }
701 }
702 }
703 catch (const exception &e)
704 {
705 cerr << "SELECT 1 FROM `" << table << "` WHERE 1" << where << " LIMIT 1\n\n";
706 cerr << "SQL query failed: " << e.what() << endl;
707 return 6;
708 }
709
710 if (print_select)
711 cout << "SELECT 1 FROM `" << table << "` WHERE 1" << where << " LIMIT 1" << endl;
712
713 try
714 {
715 if (run_delete && !fixed.empty() && !drop && !dry_run)
716 {
717 const mysqlpp::SimpleResult res =
718 connection.query("DELETE FROM `"+table+"` WHERE 1"+where).execute();
719
720 if (verbose>0)
721 cout << res.rows() << " row(s) deleted.\n" << endl;
722 }
723 }
724 catch (const exception &e)
725 {
726 cerr << "DELETE FROM `"+table+"` WHERE 1" << where << "\n\n";
727 cerr << "SQL query failed: " << e.what() << endl;
728 return 7;
729 }
730
731 if (print_delete)
732 cout << "DELETE FROM `"+table+"` WHERE 1" << where << endl;
733
734
735 // -------------------------------------------------------------------------
736
737 if (verbose>0)
738 cout << "\n---------------------------- Reading file --------------------------" << endl;
739
740 //query = update ? "UPDATE" : "INSERT";
741 query = "INSERT ";
742 if (ignore_errors)
743 query += "IGNORE ";
744 query += "`"+table+"`\n"
745 "(\n";
746
747 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
748 {
749 if (c!=vec.cbegin())
750 query += ",\n";
751
752 const size_t N = c->num;
753 for (size_t i=0; i<N; i++)
754 {
755 if (N==1)
756 query += " `"+c->column+"`";
757 else
758 query += " `"+c->column+"["+to_string(i)+"]`";
759
760 if (N>1 && i!=N-1)
761 query += ",\n";
762 }
763 }
764
765 query +=
766 "\n)\n"
767 "VALUES\n";
768
769 size_t count = 0;
770
771 const size_t num = max>0 && (max-first)<T->GetEntriesFast() ? (max-first) : T->GetEntriesFast();
772 for (size_t j=first; j<num; j++)
773 {
774 T->GetEntry(j);
775 if (has_datatype && datatype!=1)
776 continue;
777
778 if (count>0)
779 query += ",\n";
780
781 query += "(\n";
782
783 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
784 {
785 if (c!=vec.cbegin())
786 query += ",\n";
787
788 const size_t N = c->num;
789 for (size_t i=0; i<N; i++)
790 {
791 query += " "+c->fmt(i);
792
793 if (print_insert && i==0)
794 query += " /* "+c->column+" -> "+c->branch+" */";
795
796 if (N>1 && i!=N-1)
797 query += ",\n";
798 }
799 }
800 query += "\n)";
801
802 count ++;
803 }
804
805 if (!duplicate.empty())
806 query += "\nON DUPLICATE KEY UPDATE\n " + boost::join(duplicate, ",\n ");
807
808 if (verbose>0)
809 cout << count << " out of " << num << " row(s) read from file [N=" << first << ".." << num-1 << "]." << endl;
810
811 if (count==0)
812 {
813 if (verbose>0)
814 cout << "Total execution time: " << Time().UnixTime()-start.UnixTime() << "s\n" << endl;
815 return 0;
816 }
817
818 // -------------------------------------------------------------------------
819
820 if (verbose>0)
821 {
822 cout << "\n--------------------------- Inserting data -------------------------" << endl;
823 cout << "Sending INSERT query (" << query.length() << " bytes)" << endl;
824 }
825
826 try
827 {
828 if (!noinsert && !dry_run)
829 {
830 auto q = connection.query(query);
831 q.execute();
832 cout << q.info() << '\n' << endl;
833 }
834 else
835 cout << "Insert query skipped!" << endl;
836
837 if (print_insert)
838 cout << query << endl;
839 }
840 catch (const exception &e)
841 {
842 if (verbose>1 || query.length()<80*25)
843 cerr << query << "\n\n";
844 cerr << "SQL query failed (" << query.length() << " bytes):\n" << e.what() << '\n' << endl;
845 return 8;
846 }
847
848 if (verbose>0)
849 {
850 const auto sec = Time().UnixTime()-start.UnixTime();
851 cout << "Total execution time: " << sec << "s ";
852 cout << "(" << Tools::Fractional(sec/count) << "s/row)\n";
853
854 try
855 {
856 const auto resw =
857 connection.query("SHOW WARNINGS").store();
858
859 for (size_t i=0; i<resw.num_rows(); i++)
860 {
861 const mysqlpp::Row &roww = resw[i];
862
863 cout << roww["Level"] << '[' << roww["Code"] << "]: ";
864 cout << roww["Message"] << '\n';
865 }
866 cout << endl;
867
868 }
869 catch (const exception &e)
870 {
871 cerr << "\nSHOW WARNINGS\n\n";
872 cerr << "SQL query failed:\n" << e.what() << '\n' <<endl;
873 return 9;
874 }
875 }
876
877 if (print_connection)
878 {
879 try
880 {
881 // Exchange _send and _received as it is the view of the server
882 const auto &res1 = connection.query("SHOW STATUS LIKE 'Bytes_%'").store();
883 cout << left << setw(16) << res1[1]["Variable_name"] << ' ' << Tools::Scientific(res1[0]["Value"]) << endl;
884 cout << left << setw(16) << res1[0]["Variable_name"] << ' ' << Tools::Scientific(res1[1]["Value"]) << endl;
885 cout << endl;
886 }
887 catch (const exception &e)
888 {
889 cerr << "\nSHOW STATUS LIKE 'Bytes_%'\n\n";
890 cerr << "SQL query failed:\n" << e.what() << endl;
891 return 10;
892 }
893 }
894
895 return 0;
896}
Note: See TracBrowser for help on using the repository browser.