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

Last change on this file since 19180 was 19180, checked in by tbretz, 6 years ago
For debugging purpose print the client version before the connection can fail.
File size: 31.9 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 if (verbose>0)
601 {
602 cout << "Connecting to database...\n";
603 cout << "Client Version: " << mysqlpp::Connection().client_version() << endl;
604 }
605
606 Database connection(uri);
607
608 if (verbose>0)
609 cout << "Server Version: " << connection.server_version() << endl;
610
611 if (print_connection)
612 {
613 try
614 {
615 const auto &res1 = connection.query("SHOW STATUS LIKE 'Compression'").store();
616 cout << "Compression of databse connection is " << string(res1[0][1]) << endl;
617
618 const auto &res2 = connection.query("SHOW STATUS LIKE 'Ssl_cipher'").store();
619 cout << "Connection to databases is " << (string(res2[0][1]).empty()?"UNENCRYPTED":"ENCRYPTED ("+string(res2[0][1])+")") << endl;
620 }
621 catch (const exception &e)
622 {
623 cerr << "\nSHOW STATUS LIKE COMPRESSION\n\n";
624 cerr << "SQL query failed:\n" << e.what() << endl;
625 return 3;
626 }
627 }
628
629 // -------------------------------------------------------------------------
630
631 if (verbose>0)
632 cout << "\n--------------------------- Database Table -------------------------" << endl;
633
634 if (!primary.empty())
635 query += ",\n PRIMARY KEY USING BTREE (`"+boost::algorithm::join(primary, "`, `")+"`)";
636
637 if (!vindex.empty() && index)
638 query += ",\n INDEX USING BTREE (`"+boost::algorithm::join(vindex, "`, `")+"`)";
639
640 query +=
641 "\n)\n"
642 "DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci\n";
643 if (!engine.empty())
644 query += "ENGINE="+engine+"\n";
645 if (!row_format.empty())
646 query += "ROW_FORMAT="+row_format+"\n";
647 query += "COMMENT='created by "+conf.GetName()+"'\n";
648
649 // FIXME: Can we omit the catching to be able to print the
650 // query 'autmatically'?
651 try
652 {
653 if (drop)
654 {
655 // => Simple result
656 if (!dry_run)
657 connection.query("DROP TABLE `"+table+"`").execute();
658 if (verbose>0)
659 cout << "Table `" << table << "` dropped." << endl;
660 }
661 }
662 catch (const exception &e)
663 {
664 cerr << "DROP TABLE `" << table << "`\n\n";
665 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
666 return 4;
667 }
668
669 try
670 {
671 if (create && !dry_run)
672 connection.query(query).execute();
673 }
674 catch (const exception &e)
675 {
676 cerr << query << "\n\n";
677 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
678 return 5;
679 }
680
681 if (print_create)
682 cout << query << endl;
683
684 if (create && verbose>0)
685 cout << "Table `" << table << "` created." << endl;
686
687
688 try
689 {
690 if (conditional && !fixed.empty() && !drop)
691 {
692 const mysqlpp::StoreQueryResult res =
693 connection.query("SELECT 1 FROM `"+table+"` WHERE 1"+where+" LIMIT 1").store();
694
695 if (res.num_rows()>0)
696 {
697 if (verbose>0)
698 {
699 cout << "Conditional execution... detected existing rows!\n";
700 cout << "Exit.\n" << endl;
701 }
702 return 0;
703 }
704 }
705 }
706 catch (const exception &e)
707 {
708 cerr << "SELECT 1 FROM `" << table << "` WHERE 1" << where << " LIMIT 1\n\n";
709 cerr << "SQL query failed: " << e.what() << endl;
710 return 6;
711 }
712
713 if (print_select)
714 cout << "SELECT 1 FROM `" << table << "` WHERE 1" << where << " LIMIT 1" << endl;
715
716 try
717 {
718 if (run_delete && !fixed.empty() && !drop && !dry_run)
719 {
720 const mysqlpp::SimpleResult res =
721 connection.query("DELETE FROM `"+table+"` WHERE 1"+where).execute();
722
723 if (verbose>0)
724 cout << res.rows() << " row(s) deleted.\n" << endl;
725 }
726 }
727 catch (const exception &e)
728 {
729 cerr << "DELETE FROM `"+table+"` WHERE 1" << where << "\n\n";
730 cerr << "SQL query failed: " << e.what() << endl;
731 return 7;
732 }
733
734 if (print_delete)
735 cout << "DELETE FROM `"+table+"` WHERE 1" << where << endl;
736
737
738 // -------------------------------------------------------------------------
739
740 if (verbose>0)
741 cout << "\n---------------------------- Reading file --------------------------" << endl;
742
743 //query = update ? "UPDATE" : "INSERT";
744 query = "INSERT ";
745 if (ignore_errors)
746 query += "IGNORE ";
747 query += "`"+table+"`\n"
748 "(\n";
749
750 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
751 {
752 if (c!=vec.cbegin())
753 query += ",\n";
754
755 const size_t N = c->num;
756 for (size_t i=0; i<N; i++)
757 {
758 if (N==1)
759 query += " `"+c->column+"`";
760 else
761 query += " `"+c->column+"["+to_string(i)+"]`";
762
763 if (N>1 && i!=N-1)
764 query += ",\n";
765 }
766 }
767
768 query +=
769 "\n)\n"
770 "VALUES\n";
771
772 size_t count = 0;
773
774 const size_t num = max>0 && (max-first)<T->GetEntriesFast() ? (max-first) : T->GetEntriesFast();
775 for (size_t j=first; j<num; j++)
776 {
777 T->GetEntry(j);
778 if (has_datatype && datatype!=1)
779 continue;
780
781 if (count>0)
782 query += ",\n";
783
784 query += "(\n";
785
786 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
787 {
788 if (c!=vec.cbegin())
789 query += ",\n";
790
791 const size_t N = c->num;
792 for (size_t i=0; i<N; i++)
793 {
794 query += " "+c->fmt(i);
795
796 if (print_insert && i==0)
797 query += " /* "+c->column+" -> "+c->branch+" */";
798
799 if (N>1 && i!=N-1)
800 query += ",\n";
801 }
802 }
803 query += "\n)";
804
805 count ++;
806 }
807
808 if (!duplicate.empty())
809 query += "\nON DUPLICATE KEY UPDATE\n " + boost::join(duplicate, ",\n ");
810
811 if (verbose>0)
812 cout << count << " out of " << num << " row(s) read from file [N=" << first << ".." << num-1 << "]." << endl;
813
814 if (count==0)
815 {
816 if (verbose>0)
817 cout << "Total execution time: " << Time().UnixTime()-start.UnixTime() << "s\n" << endl;
818 return 0;
819 }
820
821 // -------------------------------------------------------------------------
822
823 if (verbose>0)
824 {
825 cout << "\n--------------------------- Inserting data -------------------------" << endl;
826 cout << "Sending INSERT query (" << query.length() << " bytes)" << endl;
827 }
828
829 try
830 {
831 if (!noinsert && !dry_run)
832 {
833 auto q = connection.query(query);
834 q.execute();
835 cout << q.info() << '\n' << endl;
836 }
837 else
838 cout << "Insert query skipped!" << endl;
839
840 if (print_insert)
841 cout << query << endl;
842 }
843 catch (const exception &e)
844 {
845 if (verbose>1 || query.length()<80*25)
846 cerr << query << "\n\n";
847 cerr << "SQL query failed (" << query.length() << " bytes):\n" << e.what() << '\n' << endl;
848 return 8;
849 }
850
851 if (verbose>0)
852 {
853 const auto sec = Time().UnixTime()-start.UnixTime();
854 cout << "Total execution time: " << sec << "s ";
855 cout << "(" << Tools::Fractional(sec/count) << "s/row)\n";
856
857 try
858 {
859 const auto resw =
860 connection.query("SHOW WARNINGS").store();
861
862 for (size_t i=0; i<resw.num_rows(); i++)
863 {
864 const mysqlpp::Row &roww = resw[i];
865
866 cout << roww["Level"] << '[' << roww["Code"] << "]: ";
867 cout << roww["Message"] << '\n';
868 }
869 cout << endl;
870
871 }
872 catch (const exception &e)
873 {
874 cerr << "\nSHOW WARNINGS\n\n";
875 cerr << "SQL query failed:\n" << e.what() << '\n' <<endl;
876 return 9;
877 }
878 }
879
880 if (print_connection)
881 {
882 try
883 {
884 // Exchange _send and _received as it is the view of the server
885 const auto &res1 = connection.query("SHOW STATUS LIKE 'Bytes_%'").store();
886 cout << left << setw(16) << res1[1]["Variable_name"] << ' ' << Tools::Scientific(res1[0]["Value"]) << endl;
887 cout << left << setw(16) << res1[0]["Variable_name"] << ' ' << Tools::Scientific(res1[1]["Value"]) << endl;
888 cout << endl;
889 }
890 catch (const exception &e)
891 {
892 cerr << "\nSHOW STATUS LIKE 'Bytes_%'\n\n";
893 cerr << "SQL query failed:\n" << e.what() << endl;
894 return 10;
895 }
896 }
897
898 return 0;
899}
Note: See TracBrowser for help on using the repository browser.