source: trunk/FACT++/src/rootifysql.cc@ 19140

Last change on this file since 19140 was 19139, checked in by tbretz, 7 years ago
Found a nicer way to add arguments to database URI
File size: 32.0 KB
Line 
1#include "Database.h"
2
3#include <regex>
4
5#include <boost/algorithm/string/join.hpp>
6#include <boost/tokenizer.hpp>
7#include <boost/algorithm/string.hpp>
8
9#include "tools.h"
10#include "Time.h"
11#include "Configuration.h"
12
13#include <TROOT.h>
14#include <TSystem.h>
15#include <TFile.h>
16#include <TTree.h>
17
18using namespace std;
19
20// ------------------------------------------------------------------------
21
22void SetupConfiguration(Configuration &conf)
23{
24 po::options_description control("Rootify SQL");
25 control.add_options()
26 ("uri,u", var<string>()->required(), "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
27 ("query,q", var<string>(""), "MySQL query (overwrites --file)")
28 ("file", var<string>("rootify.sql"), "An ASCII file with the MySQL query (overwrites --query)")
29 ("ignore-null,i", po_switch(), "Do not skip rows containing any NULL field")
30 ("out,o", var<string>("rootify.root"), "Output root file name")
31 ("force,f", po_switch(), "Force overwriting an existing root file ('RECREATE')")
32 ("update", po_switch(), "Update an existing root file with the new tree ('UPDATE')")
33 ("compression,c", var<uint16_t>(1), "zlib compression level for the root file")
34 ("tree,t", var<string>("Result"), "Name of the root tree")
35 ("ignore", vars<string>(), "Ignore the given columns")
36 ("display,d", po_switch(), "Displays contents on the screen (most usefull in combination with mysql statements as SHOW or EXPLAIN)")
37 ("write,w", var<string>(""), "Write output to an ascii file")
38 ("null,n", po_switch(), "Redirect the output file to /dev/null (mainly for debugging purposes, e.g. performance studies)")
39 ("no-fill", po_switch(), "Do not fill events into the root file (mainly for debugging purposes, e.g. performance studies)")
40 ("delimiter", var<string>(""), "The delimiter used if contents are displayed with --display (default=\\t)")
41 ("explain", po_switch(), "Requests an EXPLAIN from the server (shows the server optimized query)\nsee also https://dev.mysql.com/doc/refman/explain-output.html")
42 ("profiling", po_switch(), "Turn on profiling and print profile")
43 ("var.*", var<string>(), "Predefined SQL user variables (@VAR)")
44 ("env.*", vars<string>(), "Predefined environment for substitutions in the query ($ENV)")
45 ("list.*", var<string>(), "Predefined environment for substitutions in the query ($ENV). The list is read from the given file (one list entry per line)")
46 ("print-connection", po_switch(), "Print database connection information")
47 ("verbose,v", var<uint16_t>(1), "Verbosity (0: quiet, 1: default, 2: more, 3, ...)")
48 ;
49
50 po::positional_options_description p;
51 p.add("file", 1); // The 1st positional options (n=1)
52 p.add("out", 1); // The 2nd positional options (n=1)
53
54 conf.AddOptions(control);
55 conf.SetArgumentPositions(p);
56}
57
58void PrintUsage()
59{
60 cout <<
61 "rootifysql - Converts the result of a mysql query into a root file\n"
62 "\n"
63 "For convenience, this documentation uses the extended version of the options, "
64 "refer to the output below to get the abbreviations.\n"
65 "\n"
66 "Writes the result of a mysql query into a root file. For each column, a branch is "
67 "created of type double with the field name as name. This is usually the column name "
68 "if not specified otherwise by the AS mysql directive.\n"
69 "\n"
70 "Columns with CHAR or VARCHAR as field type are ignored. DATETIME, DATE and TIME "
71 "columns are converted to unix time (time_t). Rows containing any file which is "
72 "NULL are skipped if not suppressed by the --ignore-null option. Ideally, the query "
73 "is compiled in a way that no NULL field is returned. With the --display option the "
74 "result of the request is printed on the screen (NULL skipping still in action). "
75 "This can be useful to create an ascii file or to show results as 'SHOW DATABASES' "
76 "or 'EXPLAIN table'. To redirect the contents into an ascii file, the option -v0 "
77 "is useful. To suppredd writing to an output file --null can be used.\n"
78 "\n"
79 "The default is to read the query from a file called rootify.sql. Except if a different "
80 "filename is specified by the --file option or a query is given with --query.\n"
81 "\n"
82 "As a trick, the rootify.sql file can be made excutable (chmod u+x rootify.sql). "
83 "If the first line contains '#!rootifysql', the script can be executed directly.\n"
84 "\n"
85 "Columns whose name start with @ are skipped. If you want them in your output file "
86 "give them a name using AS, e.g. 'SELECT @A:=5 AS A'.\n"
87 "\n"
88 "You can use variables in your sql query like @MyVar and define them on the "
89 "command line. In this example with --var.MyVar=5\n"
90 "\n"
91 "You can use environment definitions for substitutions in your SQL query. "
92 "For example --env.TEST=5 would replace $TEST or ${TEST} in your query by 5."
93 "If you specifiy one environmentvariable more than once, a list is created. "
94 "For example --env.TEST=1 --env.TEST=2 --env.TEST=3 would substitute "
95 "$TEST or ${TEST} by '1, 2, 3'. This is useful for the SQL `IN` keyword. "
96 "You can also read the values for an enviroment substitution from a file "
97 "(one element per line), e.g. --env.TEST=file.txt. Empty lines and lines "
98 "starting with a # are skipped.\n"
99 "\n"
100 "Comments in the query-file can be placed according to the SQL standard inline "
101 "/*comment*/ or introduced with # (shell script style) or -- (SQL style).\n"
102 "\n"
103 "In case of succes, 0 is returned, a value>0 otherwise.\n"
104 "\n"
105 "Usage: rootifysql [rootify.sql [rootify.root]] [-u URI] [-q query|-f file] [-i] [-o out] [-f] [-cN] [-t tree] [-vN]\n"
106 "\n"
107 ;
108 cout << endl;
109}
110
111struct ExplainParser
112{
113 string sql;
114
115 vector<string> vec;
116
117 string substitute(string _str, const regex &expr)
118 {
119 smatch match;
120 while (regex_search(_str, match, expr, regex_constants::format_first_only))
121 {
122 const auto &len = match.length(0);
123 const auto &pos = match.position(0);
124 const auto &str = match.str(0);
125
126 const auto it = find(vec.cbegin(), vec.cend(), str);
127 const size_t id = it==vec.cend() ? vec.size() : it-vec.cbegin();
128
129 _str.replace(pos, len, "{"+to_string(id)+"}");
130
131 if (it==vec.cend())
132 vec.push_back(str);//.substr(1, str.size()-2));
133 }
134
135 return _str;
136 }
137
138 string substitute(const string &str, const string &expr)
139 {
140 return substitute(str, regex(expr));
141 }
142
143 vector<string> queries;
144
145 string resub(string str)
146 {
147 // search for "KEYWORD expression"
148 regex reg("\\{[0-9]+\\}");
149
150 //smatch match;
151 smatch match;
152 while (regex_search(str, match, reg, regex_constants::format_first_only))
153 {
154 const auto &len = match.length(0);
155 const auto &pos = match.position(0);
156 const auto &arg = match.str(0); // Argument
157
158 const auto idx = atoi(arg.c_str()+1);
159
160 str.replace(pos, len, resub(vec[idx]));
161 }
162
163 return str;
164 }
165
166 void expression(string expr, size_t indent=0)
167 {
168 if (expr[0]=='{')
169 {
170 const auto idx = atoi(expr.c_str()+1);
171
172 // This is a subquery
173 if (vec[idx].substr(0,3)=="(/*")
174 {
175 cout << setw(indent) << ' ' << "(\n";
176 find_tokens(vec[idx], indent+4);
177 cout << setw(indent) << ' ' << ") ";
178 }
179 else
180 // This is just something to substitute back
181 if (vec[idx].substr(0,2)=="({")
182 {
183 cout << setw(indent) << ' ' << "(" << resub(vec[idx]) << ") ";
184 }
185 else
186 {
187 if (indent>0)
188 cout << setw(indent) << ' ';
189 cout << resub(vec[idx]);
190 }
191 }
192 else
193 {
194 if (indent>0)
195 cout << setw(indent) << ' ';
196 cout << resub(expr);
197 }
198 }
199
200 void find_tokens(string str, size_t indent=0)
201 {
202 // ( COMMENT )?( TOKEN )?(( {NNN} | NNN )( AS|ON ( {NNN}) ))?(,)?)
203 //regex reg("(\\/\\*\\ select\\#[0-9]+\\ \\*\\/\\ *)?([a-zA-Z ]+)?((\\{[0-9]+\\}|[0-9]+)(\\ ?([Aa][Ss]|[Oo][Nn])\\ ?(\\{[0-9]+\\}))?(,)?)");
204
205 const string _com = "\\/\\*\\ select\\#[0-9]+\\ \\*\\/\\ *";
206
207 const string _tok = "[a-zA-Z_ ]+";
208
209 const string _nnn = "\\{[0-9]+\\}|[0-9]+";
210
211 const string _as = "\\ ?([Aa][Ss])\\ ?";
212
213 // ( _nnn ) ( _as ( _nnn ))?(,)? // can also match noting in between two {NNN}
214 const string _exp = "("+_nnn+")" + "("+_as+"("+_nnn+"))?(,)?";
215
216 // Matche: ( _com )? ( ( _tok )? ( _exp ) | ( _tok ) )
217 regex reg("("+_com+")?" + "(" + "("+_tok+")?"+"("+_exp+")" + "|" + "("+_tok+")" + ")");
218
219 smatch match;
220 while (regex_search(str, match, reg, regex_constants::format_first_only))
221 {
222
223 const auto &com = match.str(1); // comment
224 const auto &tok1 = Tools::Trim(match.str(3)); // token with expression
225 const auto &arg1 = match.str(5); // argument 1
226 const auto &as = match.str(7); // as
227 const auto &arg2 = match.str(8); // argument 2
228 const auto &comma = match.str(9); // comma
229 const auto &tok2 = Tools::Trim(match.str(10)); // token without expression
230
231 if (!com.empty())
232 cout << setw(indent) << ' ' << "\033[34m" << com << "\033[0m" << '\n';
233
234 if (!tok1.empty())
235 cout << setw(indent) << ' ' << "\033[32m" << tok1 << "\033[0m" << '\n';
236 if (!tok2.empty())
237 cout << setw(indent) << ' ' << "\033[32m" << tok2 << "\033[0m" << '\n';
238
239 if (!arg1.empty())
240 {
241 expression(arg1, indent+4);
242
243 if (!as.empty())
244 cout << " \033[33m" << as << "\033[0m ";
245
246 if (!arg2.empty())
247 expression(arg2);
248
249 if (!comma.empty())
250 cout << ',';
251
252 cout << '\n';
253 }
254
255 str = str.substr(match.position(0)+match.length(0));
256 }
257 }
258
259
260 ExplainParser(const string &_sql) : sql(_sql)
261 {
262 // substitute all strings
263 sql = substitute(sql, "'[^']*'");
264
265 // substitute all escaped sequences (`something`.`something-else`)
266 sql = substitute(sql, "`[^`]*`(\\.`[^`]*`)*");
267
268 // substitute all paranthesis
269 sql = substitute(sql, "[a-zA-Z0-9_]*\\([^\\(\\)]*\\)");
270
271 //cout << sql << "\n\n";
272 find_tokens(sql);
273 cout << endl;
274 }
275};
276
277// Remove queries...
278void format(string sql)
279{
280 ExplainParser p(sql);
281
282 /*
283
284 SELECT
285 [ALL | DISTINCT | DISTINCTROW ]
286 [HIGH_PRIORITY]
287 [STRAIGHT_JOIN]
288 [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
289 [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
290 select_expr [, select_expr ...]
291 [FROM table_references
292 [PARTITION partition_list]
293 [WHERE where_condition]
294 [GROUP BY {col_name | expr | position}, ... [WITH ROLLUP]]
295 [HAVING where_condition]
296 [WINDOW window_name AS (window_spec)
297 [, window_name AS (window_spec)] ...]
298 [ORDER BY {col_name | expr | position}
299 [ASC | DESC], ... [WITH ROLLUP]]
300 [LIMIT {[offset,] row_count | row_count OFFSET offset}]
301 [INTO OUTFILE 'file_name'
302 [CHARACTER SET charset_name]
303 export_options
304 | INTO DUMPFILE 'file_name'
305 | INTO var_name [, var_name]]
306 [FOR {UPDATE | SHARE} [OF tbl_name [, tbl_name] ...] [NOWAIT | SKIP LOCKED]
307 | LOCK IN SHARE MODE]]
308 */
309
310 /*
311table_references:
312 escaped_table_reference [, escaped_table_reference] ...
313
314escaped_table_reference:
315 table_reference
316 | { OJ table_reference }
317
318table_reference:
319 table_factor
320 | join_table
321
322table_factor:
323 tbl_name [PARTITION (partition_names)]
324 [[AS] alias] [index_hint_list]
325 | table_subquery [AS] alias [(col_list)]
326 | ( table_references )
327
328join_table:
329 table_reference [INNER | CROSS] JOIN table_factor [join_condition]
330 | table_reference STRAIGHT_JOIN table_factor
331 | table_reference STRAIGHT_JOIN table_factor ON conditional_expr
332 | table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_condition
333 | table_reference NATURAL [INNER | {LEFT|RIGHT} [OUTER]] JOIN table_factor
334
335join_condition:
336 ON conditional_expr
337 | USING (column_list)
338
339index_hint_list:
340 index_hint [, index_hint] ...
341
342index_hint:
343 USE {INDEX|KEY}
344 [FOR {JOIN|ORDER BY|GROUP BY}] ([index_list])
345 | IGNORE {INDEX|KEY}
346 [FOR {JOIN|ORDER BY|GROUP BY}] (index_list)
347 | FORCE {INDEX|KEY}
348 [FOR {JOIN|ORDER BY|GROUP BY}] (index_list)
349
350index_list:
351 index_name [, index_name] ...
352 */
353
354}
355
356int finish(Database &connection, const uint16_t &verbose, const bool &profiling, const bool &print_connection)
357{
358 if (verbose>0)
359 {
360 try
361 {
362 const auto resw =
363 connection.query("SHOW WARNINGS").store();
364
365 if (resw.num_rows()>0)
366 cout << "\n" << resw.num_rows() << " Warning(s) issued:\n\n";
367
368 for (size_t i=0; i<resw.num_rows(); i++)
369 {
370 const mysqlpp::Row &roww = resw[i];
371
372 cout << roww["Level"] << '[' << roww["Code"] << "]: ";
373 cout << roww["Message"] << '\n';
374 }
375 cout << endl;
376
377 }
378 catch (const exception &e)
379 {
380 cerr << "\nSHOW WARNINGS\n\n";
381 cerr << "SQL query failed:\n" << e.what() << endl;
382 return 6;
383 }
384 }
385
386 if (profiling)
387 {
388 try
389 {
390 const auto N =
391 connection.query("SHOW PROFILES").store().num_rows();
392
393 const auto resp =
394 connection.query("SHOW PROFILE ALL FOR QUERY "+to_string(verbose?N-1:N)).store();
395
396 cout << '\n';
397 cout << left;
398 cout << setw(26) << "Status" << ' ';
399 cout << right;
400 cout << setw(11) << "Duration" << ' ';
401 cout << setw(11) << "CPU User" << ' ';
402 cout << setw(11) << "CPU System" << '\n';
403 cout << "--------------------------------------------------------------\n";
404 for (size_t i=0; i<resp.num_rows(); i++)
405 {
406 const mysqlpp::Row &rowp = resp[i];
407
408 cout << left;
409 cout << setw(26) << rowp["Status"] << ' ';
410 cout << right;
411 cout << setw(11) << rowp["Duration"] << ' ';
412 cout << setw(11) << rowp["CPU_user"] << ' ';
413 cout << setw(11) << rowp["CPU_system"] << '\n';
414 }
415 cout << "--------------------------------------------------------------\n";
416 cout << endl;
417 }
418 catch (const exception &e)
419 {
420 cerr << "\nSHOW PROFILE ALL\n\n";
421 cerr << "SQL query failed:\n" << e.what() << '\n' <<endl;
422 return 11;
423 }
424 }
425
426 if (print_connection)
427 {
428 try
429 {
430 // Exchange _send and _received as it is the view of the server
431 const auto &res1 = connection.query("SHOW STATUS LIKE 'Bytes_%'").store();
432 cout << left << setw(16) << res1[1]["Variable_name"] << ' ' << Tools::Scientific(res1[0]["Value"]) << endl;
433 cout << left << setw(16) << res1[0]["Variable_name"] << ' ' << Tools::Scientific(res1[1]["Value"]) << endl;
434 cout << endl;
435 }
436 catch (const exception &e)
437 {
438 cerr << "\nSHOW STATUS LIKE 'Bytes_%'\n\n";
439 cerr << "SQL query failed:\n" << e.what() << endl;
440 return 6;
441 }
442 }
443
444 return 0;
445
446}
447
448int main(int argc, const char* argv[])
449{
450 Time start;
451
452 gROOT->SetBatch();
453
454 Configuration conf(argv[0]);
455 conf.SetPrintUsage(PrintUsage);
456 SetupConfiguration(conf);
457
458 if (!conf.DoParse(argc, argv))
459 return 127;
460
461 // ----------------------------- Evaluate options --------------------------
462 const string uri = conf.Get<string>("uri");
463 const string out = conf.Get<string>("out");
464 const string file = conf.Get<string>("file");
465 const string tree = conf.Get<string>("tree");
466 const bool force = conf.Get<bool>("force");
467 const bool ignorenull = conf.Get<bool>("ignore-null");
468 const bool update = conf.Get<bool>("update");
469 const bool display = conf.Get<bool>("display");
470 const string write = conf.Get<string>("write");
471 const bool noout = conf.Get<bool>("null");
472 const bool nofill = conf.Get<bool>("no-fill");
473 const bool explain = conf.Get<bool>("explain");
474 const bool profiling = conf.Get<bool>("profiling");
475 const uint16_t verbose = conf.Get<uint16_t>("verbose");
476 const uint16_t compression = conf.Get<uint16_t>("compression");
477 const string delimiter = conf.Get<string>("delimiter");
478 const vector<string> _ignore = conf.Vec<string>("ignore");
479 const bool print_connection = conf.Get<bool>("print-connection");
480 //const vector<Map> mymap = conf.Vec<Map>("map");
481
482 // -------------------------------------------------------------------------
483
484 const auto vars = conf.GetWildcardOptions("var.*");
485
486 vector<string> variables;
487 for (const auto &var : vars)
488 variables.emplace_back('@'+var.substr(4)+":="+Tools::Trim(conf.Get<string>(var)));
489
490 // -------------------------------------------------------------------------
491
492 if (verbose>0)
493 cout << "\n------------------------ Rootify SQL -------------------------" << endl;
494
495 string query = conf.Get<string>("query");
496 if (query.empty())
497 {
498 if (verbose>0)
499 cout << "Reading query from file '" << file << "'." << endl;
500
501 ifstream fin(file);
502 if (!fin)
503 {
504 cerr << "Could not open query in '" << file << "': " << strerror(errno) << endl;
505 return 1;
506 }
507 getline(fin, query, (char)fin.eof());
508 }
509
510 if (query.empty())
511 {
512 cerr << "No query specified." << endl;
513 return 2;
514 }
515
516 // -------------------------------------------------------------------------
517
518 map<string, vector<string>> envs;
519
520 for (const auto &env : conf.GetWildcardOptions("env.*"))
521 envs[env.substr(4)] = conf.Vec<string>(env);
522
523 for (const auto &env : conf.GetWildcardOptions("list.*"))
524 {
525 const string fname = conf.Get<string>(env);
526 const string &ident = env.substr(5);
527
528 ifstream fin(fname);
529 if (!fin)
530 {
531 cerr << "Could not open environment in '" << fname << "' for ${" << ident << "}: " << strerror(errno) << endl;
532 return 3;
533 }
534
535 for (string line; getline(fin, line); )
536 {
537 const auto &l = Tools::Trim(line);
538 if (!l.empty() && l[0]!='#')
539 envs[ident].push_back(line);
540 }
541
542 if (verbose>0)
543 cout << "Found " << envs[ident].size() << " list element(s) for ${" << ident << "}" << endl;
544 }
545
546 for (const auto &env : envs)
547 {
548 regex rexpr("\\$(\\{"+env.first+"\\}|"+env.first+"\\b)");
549 query = regex_replace(query, rexpr, boost::join(env.second, ", "));
550 }
551
552 // -------------------------- Check for file permssion ---------------------
553 // Strictly speaking, checking for write permission and existance is not necessary,
554 // but it is convenient that the user does not find out that it failed after
555 // waiting long for the query result
556 //
557 // I am using root here instead of boost to be
558 // consistent with the access pattern by TFile
559 TString path(noout?"/dev/null":out.c_str());
560 gSystem->ExpandPathName(path);
561
562 if (!noout)
563 {
564 FileStat_t stat;
565 const Int_t exist = !gSystem->GetPathInfo(path, stat);
566 const Bool_t _write = !gSystem->AccessPathName(path, kWritePermission) && R_ISREG(stat.fMode);
567
568 if ((update && !exist) || (update && exist && !_write) || (force && exist && !_write))
569 {
570 cerr << "File '" << path << "' is not writable." << endl;
571 return 3;
572 }
573
574 if (!update && !force && exist)
575 {
576 cerr << "File '" << path << "' already exists." << endl;
577 return 4;
578 }
579 }
580
581 Time start2;
582
583 // --------------------------- Connect to database -------------------------------------------------
584
585 if (query.back()!='\n')
586 query += '\n';
587
588 if (verbose>0)
589 cout << "Connecting to database..." << endl;
590
591
592 Database connection(uri); // Keep alive while fetching rows
593
594 if (verbose>0)
595 {
596 cout << "Client Version: " << connection.client_version() << '\n';
597 cout << "Server Version: " << connection.server_version() << endl;
598 }
599
600 if (print_connection)
601 {
602 try
603 {
604 const auto &res1 = connection.query("SHOW STATUS LIKE 'Compression'").store();
605 cout << "Compression of databse connection is " << string(res1[0][1]) << endl;
606
607 const auto &res2 = connection.query("SHOW STATUS LIKE 'Ssl_cipher'").store();
608 cout << "Connection to databases is " << (string(res2[0][1]).empty()?"UNENCRYPTED":"ENCRYPTED ("+string(res2[0][1])+")") << endl;
609 }
610 catch (const exception &e)
611 {
612 cerr << "\nSHOW STATUS LIKE 'Compression'\n\n";
613 cerr << "SQL query failed:\n" << e.what() << endl;
614 return 6;
615 }
616 }
617
618 try
619 {
620 if (profiling)
621 connection.query("SET PROFILING=1").execute();
622 }
623 catch (const exception &e)
624 {
625 cerr << "\nSET profiling=1\n\n";
626 cerr << "SQL query failed:\n" << e.what() << endl;
627 return 6;
628 }
629
630 // -------------------------- Set user defined variables -------------------
631 if (variables.size()>0)
632 {
633 if (verbose>0)
634 cout << "Setting user defined variables..." << endl;
635
636 const string varset =
637 "SET\n "+boost::algorithm::join(variables, ",\n ");
638
639 try
640 {
641 connection.query(varset).execute();
642 }
643 catch (const exception &e)
644 {
645 cerr << '\n' << varset << "\n\n";
646 cerr << "SQL query failed:\n" << e.what() << endl;
647 return 7;
648 }
649
650 if (verbose>2)
651 cout << '\n' << varset << '\n' << endl;
652 }
653
654 // ------------------------- Explain query if requested --------------------
655
656 if (explain)
657 {
658 try
659 {
660 const auto res0 =
661 connection.query("EXPLAIN FORMAT=JSON "+query).store();
662
663 cout << res0[0][0] << endl;
664 cout << endl;
665
666 const mysqlpp::StoreQueryResult res1 =
667 connection.query("EXPLAIN "+query).store();
668
669 for (size_t i=0; i<res1.num_rows(); i++)
670 {
671 const mysqlpp::Row &row = res1[i];
672
673 cout << "\nid : " << row["id"];
674 cout << "\nselect type : " << row["select_type"];
675
676 if (!row["table"].is_null())
677 cout << "\ntable : " << row["table"];
678
679 if (!row["partitions"].is_null())
680 cout << "\npartitions : " << row["partitions"];
681
682 if (!row["key"].is_null())
683 cout << "\nselected key : " << row["key"] << " [len=" << row["key_len"] << "] out of (" << row["possible_keys"] << ")";
684
685 if (!row["type"].is_null())
686 cout << "\njoin type : " << row["type"];
687
688 //if (!row["possible_keys"].is_null())
689 // cout << "\npossible_keys: " << row["possible_keys"];
690
691 //if (!row["key_len"].is_null())
692 // cout << "\nkey_len : " << row["key_len"];
693
694 if (!row["ref"].is_null())
695 cout << "\nref : (" << row["ref"] << ") compared to the index";
696
697 if (!row["rows"].is_null())
698 cout << "\nrows : " << row["rows"];
699
700 if (!row["filtered"].is_null())
701 cout << "\nfiltered : " << row["filtered"];
702
703 if (!row["extra"].is_null())
704 cout << "\nExtra : " << row["extra"];
705
706 cout << endl;
707 }
708
709 cout << endl;
710
711 const mysqlpp::StoreQueryResult res2 =
712 connection.query("SHOW WARNINGS").store();
713
714 for (size_t i=0; i<res2.num_rows(); i++)
715 {
716 const mysqlpp::Row &row = res2[i];
717
718 // 1003 //
719 cout << row["Level"] << '[' << row["Code"] << "]:\n";
720 if (uint32_t(row["Code"])==1003)
721 format(row["Message"].c_str());
722 else
723 cout << row["Message"] << '\n' << endl;
724
725 }
726
727 }
728 catch (const exception &e)
729 {
730 cerr << '\n' << query << "\n\n";
731 cerr << "SQL query failed:\n" << e.what() << endl;
732 return 8;
733 }
734
735 return 0;
736 }
737
738 // -------------------------- Request data from database -------------------
739 if (verbose>0)
740 cout << "Requesting data..." << endl;
741
742 if (verbose>2)
743 cout << '\n' << query << endl;
744
745 const mysqlpp::UseQueryResult res =
746 connection.query(query).use();
747
748 // -------------------------------------------------------------------------
749
750 if (verbose>0)
751 {
752 cout << "Opening file '" << path << "' [compression=" << compression << "]...\n";
753 cout << "Writing data to tree '" << tree << "'" << endl;
754 }
755
756 // ----------------------------- Open output file --------------------------
757 TFile tfile(path, update?"UPDATE":(force?"RECREATE":"CREATE"), "Rootify SQL", compression);
758 if (tfile.IsZombie())
759 return 9;
760
761 // -------------------------------------------------------------------------
762
763 // get the first row to get the field description
764 mysqlpp::Row row = res.fetch_row();
765 if (!row)
766 {
767 cerr << "Empty set returned... nothing to write." << endl;
768 finish(connection, verbose, profiling, print_connection);
769 return 10;
770 }
771
772 if (verbose>0)
773 cout << "Trying to setup " << row.size() << " branches..." << endl;
774
775 if (verbose>1)
776 cout << endl;
777
778 const mysqlpp::FieldNames &l = *row.field_list().list;
779
780 vector<double> buf(l.size());
781 vector<uint8_t> typ(l.size(),'n'); // n=number [double], d is used for DateTime
782
783 UInt_t cols = 0;
784
785
786 // -------------------- Configure branches of TTree ------------------------
787 TTree *ttree = new TTree(tree.c_str(), query.c_str());
788
789 size_t skipat = 0;
790 size_t skipreg = 0;
791 for (size_t i=0; i<l.size(); i++)
792 {
793 const string t = row[i].type().sql_name();
794
795 if (t.find("DATETIME")!=string::npos)
796 typ[i] = 'd';
797 else
798 if (t.find("DATE")!=string::npos)
799 typ[i] = 'D';
800 else
801 if (t.find("TIME")!=string::npos)
802 typ[i] = 'T';
803 else
804 if (t.find("VARCHAR")!=string::npos)
805 typ[i] = 'V';
806 else
807 if (t.find("CHAR")!=string::npos)
808 typ[i] = 'C';
809
810 bool found = false;
811 for (const auto &pattern: _ignore)
812 {
813 if (regex_match(l[i], regex(pattern)))
814 {
815 found = true;
816 typ[i] = '-';
817 skipreg++;
818 break;
819 }
820 }
821
822 if (l[i][0]=='@')
823 {
824 typ[i] = '@';
825 skipat++;
826 }
827
828 const bool use = l[i][0]!='@' && typ[i]!='V' && typ[i]!='C' && !found;
829
830 if (verbose>1)
831 cout << (use?" + ":" - ") << l[i].c_str() << " [" << t << "] {" << typ[i] << "}\n";
832
833 if (use)
834 {
835 // string name = l[i];
836 // for (const auto &m: mymap)
837 // name = boost::regex_replace(l[i], boost::regex(m.first), m.second);
838
839 ttree->Branch(l[i].c_str(), buf.data()+i);
840 cols++;
841 }
842 }
843 // -------------------------------------------------------------------------
844
845 if (verbose>1)
846 cout << endl;
847 if (verbose>0)
848 {
849 if (skipreg)
850 cout << skipreg << " branches skipped due to ignore list." << endl;
851 if (skipat)
852 cout << skipat << " branches skipped due to name starting with @." << endl;
853 cout << "Configured " << cols << " branches.\nFilling branches..." << endl;
854 }
855
856 ofstream fout(write);
857 if (!write.empty() && !fout)
858 cout << "WARNING: Writing to '" << write << "' failed: " << strerror(errno) << endl;
859
860 if (display)
861 {
862 cout << endl;
863 cout << "#";
864 for (size_t i=0; i<l.size(); i++)
865 cout << ' ' << l[i].c_str();
866 cout << endl;
867 }
868
869 if (!write.empty())
870 {
871 fout << "#";
872 for (size_t i=0; i<l.size(); i++)
873 fout << ' ' << l[i].c_str();
874 fout << endl;
875 }
876
877 // ---------------------- Fill TTree with DB data --------------------------
878 size_t count = 0;
879 size_t skip = 0;
880 do
881 {
882 count++;
883
884 ostringstream sout;
885
886 size_t idx=0;
887 for (auto col=row.begin(); col!=row.end(); col++, idx++)
888 {
889 if (display || !write.empty())
890 {
891 if (idx>0)
892 sout << (delimiter.empty()?"\t":delimiter);
893 sout << col->c_str();
894 }
895
896 if (!ignorenull && col->is_null())
897 {
898 skip++;
899 break;
900 }
901
902 switch (typ[idx])
903 {
904 case 'd':
905 buf[idx] = time_t((mysqlpp::DateTime)(*col));
906 break;
907
908 case 'D':
909 buf[idx] = time_t((mysqlpp::Date)(*col));
910 break;
911
912 case 'T':
913 buf[idx] = time_t((mysqlpp::Time)(*col));
914 break;
915
916 case 'V':
917 case 'C':
918 case '-':
919 case '@':
920 break;
921
922 default:
923 buf[idx] = atof(col->c_str());
924 }
925 }
926
927 if (idx==row.size())
928 {
929 if (!nofill)
930 ttree->Fill();
931
932 if (display)
933 cout << sout.str() << endl;
934 if (!write.empty())
935 fout << sout.str() << '\n';
936 }
937
938 row = res.fetch_row();
939
940
941 } while (row);
942
943 // -------------------------------------------------------------------------
944
945 if (display)
946 cout << '\n' << endl;
947
948 if (verbose>0)
949 {
950 cout << count << " rows fetched." << endl;
951 if (skip>0)
952 cout << skip << " rows skipped due to NULL field." << endl;
953
954 cout << ttree->GetEntries() << " rows filled into tree." << endl;
955 }
956
957 ttree->Write();
958 tfile.Close();
959
960 if (verbose>0)
961 {
962 const auto sec = Time().UnixTime()-start.UnixTime();
963
964 cout << Tools::Scientific(tfile.GetSize()) << "B written to disk.\n";
965 cout << "File closed.\n";
966 cout << "Execution time: " << sec << "s ";
967 cout << "(" << Tools::Fractional(sec/count) << "s/row)\n";
968 cout << "--------------------------------------------------------------" << endl;
969 }
970
971 return finish(connection, verbose, profiling, print_connection);
972}
Note: See TracBrowser for help on using the repository browser.