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

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