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

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