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

Last change on this file since 19137 was 19137, checked in by tbretz, 6 years ago
Bytes received and sent in the view of the client.
File size: 24.6 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[/comp].")
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 ;
72
73 po::options_description debug("Debug options");
74 debug.add_options()
75 ("no-insert", po_switch(), "Does not insert any data into the table")
76 ("dry-run", po_switch(), "Skip any query which changes the databse (might result in consecutive failures)")
77 ("print-connection", po_switch(), "Print database connection information")
78 ("print-branches", po_switch(), "Print the branches found in the tree")
79 ("print-leaves", po_switch(), "Print the leaves found in the tree (this is what is processed)")
80 ("print-insert", po_switch(), "Print the INSERT query (note that it contains all data)")
81 ("print-create", po_switch(), "Print the CREATE query")
82 ("verbose,v", var<uint16_t>(1), "Verbosity (0: quiet, 1: default, 2: more, 3, ...)")
83 ;
84
85 po::positional_options_description p;
86 p.add("file", 1); // The 1st positional options (n=1)
87
88 conf.AddOptions(control);
89 conf.AddOptions(debug);
90 conf.SetArgumentPositions(p);
91}
92
93void PrintUsage()
94{
95 cout <<
96 "root2sql - Fills the data from a root file into a database\n"
97 "\n"
98 "For convenience, this documentation uses the extended version of the options, "
99 "refer to the output below to get the abbreviations.\n"
100 "\n"
101 "This is a general purpose tool to fill the contents of a root file into a database "
102 "as long as this is technically possible and makes sense. Note that root can even "
103 "write complex data like a TH1F into a database, this is not the purpose of this "
104 "program.\n"
105 "\n"
106 "Each root tree has branches and leaves (the basic data types). These leaves can "
107 "be read independently of the classes which were used to write the root file. "
108 "The default tree to read from is 'Events' but the name can be overwritten "
109 "using --tree. The default table name to fill the data into is identical to "
110 "the tree name. It can be overwritten using --table.\n"
111 "\n"
112 "The name of each column to which data is filled from a leave is obtained from "
113 "the leaves' names. The leave names can be checked using --print-leaves. "
114 "A --print-branches exists for convenience to print only the high-level branches. "
115 "Sometimes these names might be quite unconvenient like MTime.fTime.fMilliSec or "
116 "just MHillas.fWidth. To allow to simplify column names, regular expressions "
117 "(using boost's regex) can be defined to change the names. Note that these regular "
118 "expressions are applied one by one on each leaf's name. A valid expression could "
119 "be:\n"
120 " --map=MHillas\\.f/\n"
121 "which would remove all occurances of 'MHillas.f'. This option can be used more than "
122 "once. They are applied in sequence. A single match does not stop the sequence.\n"
123 "\n"
124 "Sometimes it might also be convenient to skip a leaf. This can be done with "
125 "the --ignore resource. If the given regular expresion yields a match, the "
126 "leaf will be ignored. Note that the regular expression works on the raw-name "
127 "of the leaf not the readily mapped SQL column names. Example:\n"
128 " --ignore=ThetaSq\\..*\n"
129 "will skip all leaved which start with 'ThetaSq.'. This option can be used"
130 "more than once.\n"
131 "\n"
132 "The data type of each column is kept as close as possible to the leaves' data "
133 "types. If for some reason this is not wanted, the data type of the SQL column "
134 "can be overwritten with --sql-type sql-column/sql-ytpe, for example:\n"
135 " --sql-type=FileId/UNSIGNED INT\n"
136 "while the first argument of the name of the SQL column to which the data type "
137 "should be applied. The second column is the basic SQL data type. The option can "
138 "be given more than once.\n"
139 "\n"
140 "Database interaction:\n"
141 "\n"
142 "To drop an existing table, --drop can be used.\n"
143 "\n"
144 "To create a table according to theSQL column names and data types, --create "
145 "can be used. The query used can be printed with --print-create even --create "
146 "has not been specified.\n"
147 "\n"
148 "To choose the columns which should become primary keys, use --primary, "
149 "for exmaple:\n"
150 " --primary=col1\n"
151 "To define more than one column as primary key, the option can be given more than "
152 "once. Note that the combination of these columns must be unique.\n"
153 "\n"
154 "All columns are created as NOT NULL as default. To force a database engine "
155 "and/or a storage format, use --engine and --rot-format.\n"
156 "\n"
157 "Usually, the INSERT query would fail if the PRIMARY key exists already. "
158 "This can be avoided using the 'ON DUPLICATE KEY UPDATE' directive. With the "
159 "--duplicate, you can specify what should be updated in case of a duplicate key. "
160 "To keep the row untouched, you can just update the primary key "
161 "with the identical primary key, e.g. --duplicate='MyPrimary=VALUES(MyPrimary)'. "
162 "The --duplicate resource can be specified more than once to add more expressions "
163 "to the assignment_list. For more details, see the MySQL manual.\n"
164 "\n"
165 "For debugging purpose, or to just create or drop a table, the final insert "
166 "query can be skipped using --no-insert. Note that for performance reason, "
167 "all data is collected in memory and a single INSERT query is issued at the "
168 "end.\n"
169 "\n"
170 "Another possibility is to add the IGNORE keyword to the INSERT query by "
171 "--ignore-errors, which essentially ignores all errors and turns them into "
172 "warnings which are printed after the query succeeded.\n"
173 "\n"
174 "Using a higher verbosity level (-v), an overview of the written columns or all "
175 "processed leaves is printed depending on the verbosity level. The output looks "
176 "like the following\n"
177 " Leaf name [root data type] (SQL name)\n"
178 "for example\n"
179 " MTime.fTime.fMilliSec [Long64_t] (MilliSec)\n"
180 "which means that the leaf MTime.fTime.fMilliSec is detected to be a Long64_t "
181 "which is filled into a column called MilliSec. Leaves with non basic data types "
182 "are ignored automatically and are marked as (-n/a-). User ignored columnd "
183 "are marked as (-ignored-).\n"
184 "\n"
185 "If a query failed, the query is printed to stderr together with the error message. "
186 "For the main INSERT query, this is only true if the verbosity level is at least 2 "
187 "or the query has less than 80*25 bytes.\n"
188 "\n"
189 "In case of succes, 0 is returned, a value>0 otherwise.\n"
190 "\n"
191 "Usage: root2sql [options] -uri URI rootfile.root\n"
192 "\n"
193 ;
194 cout << endl;
195}
196
197enum BasicType_t
198{
199 kNone = 0,
200 kFloat,
201 kDouble,
202 kInt16,
203 kUInt16,
204 kInt32,
205 kUInt32,
206 kInt64,
207 kUInt64,
208};
209
210static const map<string, pair<BasicType_t, string>> ConvRoot =
211{
212 { "Float_t", { kFloat, "FLOAT" } },
213 { "Double_t", { kDouble, "DOUBLE" } },
214 { "ULong64_t", { kUInt64, "BIGINT UNSIGNED" } },
215 { "Long64_t", { kInt64, "BIGINT" } },
216 { "UInt_t", { kUInt32, "INT UNSIGNED" } },
217 { "Int_t", { kInt32, "INT" } },
218 { "UShort_t", { kUInt16, "SMALLINT UNSIGNED" } },
219 { "Short_t", { kInt16, "SMALLINT" } },
220};
221
222struct Container
223{
224 string branch; // branch name
225 string column; // column name
226 BasicType_t type;
227 void *ptr;
228
229 Container(const string &b, const string &c, const BasicType_t &t) : branch(b), column(c), type(t), ptr(0)
230 {
231 switch (t)
232 {
233 case kFloat: ptr = new Float_t; break;
234 case kDouble: ptr = new Double_t; break;
235 case kInt16: ptr = new Short_t; break;
236 case kUInt16: ptr = new UShort_t; break;
237 case kInt32: ptr = new Int_t; break;
238 case kUInt32: ptr = new UInt_t; break;
239 case kInt64: ptr = new Long64_t; break;
240 case kUInt64: ptr = new ULong64_t; break;
241 case kNone:
242 break;
243 }
244 }
245 ~Container()
246 {
247 //::operator delete(ptr); // It seems root is deleting it already
248 }
249
250 string fmt() const
251 {
252 ostringstream str;
253
254 switch (type)
255 {
256 case kFloat: str << setprecision(8) << *reinterpret_cast<Float_t*>(ptr); break;
257 case kDouble: str << setprecision(16) << *reinterpret_cast<Double_t*>(ptr); break;
258 case kInt16: str << *reinterpret_cast<Short_t*>(ptr); break;
259 case kUInt16: str << *reinterpret_cast<UShort_t*>(ptr); break;
260 case kInt32: str << *reinterpret_cast<Int_t*>(ptr); break;
261 case kUInt32: str << *reinterpret_cast<UInt_t*>(ptr); break;
262 case kInt64: str << *reinterpret_cast<Long64_t*>(ptr); break;
263 case kUInt64: str << *reinterpret_cast<ULong64_t*>(ptr); break;
264 case kNone:
265 break;
266 }
267
268 //if (str.str()=="nan" || str.str()=="-nan" || str.str()=="inf" || str.str()=="-inf")
269 // return "NULL";
270
271 return str.str();
272 }
273};
274
275void ErrorHandlerAll(Int_t level, Bool_t abort, const char *location, const char *msg)
276{
277 if (string(msg).substr(0,24)=="no dictionary for class ")
278 return;
279
280 DefaultErrorHandler(level, abort, location, msg);
281}
282
283int main(int argc, const char* argv[])
284{
285 Time start;
286
287 gROOT->SetBatch();
288 SetErrorHandler(ErrorHandlerAll);
289
290 Configuration conf(argv[0]);
291 conf.SetPrintUsage(PrintUsage);
292 SetupConfiguration(conf);
293
294 if (!conf.DoParse(argc, argv))
295 return 127;
296
297 // ----------------------------- Evaluate options --------------------------
298 const string uri = conf.Get<string>("uri");
299 const string file = conf.Get<string>("file");
300 const string tree = conf.Get<string>("tree");
301 const string table = conf.Get<string>("table").empty() ? tree : conf.Get<string>("table");
302
303 const uint16_t verbose = conf.Get<uint16_t>("verbose");
304 const int64_t first = conf.Get<int64_t>("first");
305 const int64_t max = conf.Get<int64_t>("max");
306
307 const bool force = conf.Get<bool>("force");
308 const bool drop = conf.Get<bool>("drop");
309 const bool create = conf.Get<bool>("create") || drop;
310 const bool noinsert = conf.Get<bool>("no-insert");
311 const bool dry_run = conf.Get<bool>("dry-run");
312
313 const string engine = conf.Get<string>("engine");
314 const string row_format = conf.Get<string>("row-format");
315
316 const vector<string> duplicate = conf.Vec<string>("duplicate");
317
318 const bool ignore_errors = conf.Get<bool>("ignore-errors");
319
320 const bool print_connection = conf.Get<bool>("print-connection");
321 const bool print_branches = conf.Get<bool>("print-branches");
322 const bool print_leaves = conf.Get<bool>("print-leaves");
323 const bool print_create = conf.Get<bool>("print-create");
324 const bool print_insert = conf.Get<bool>("print-insert");
325
326 const vector<Map> mymap = conf.Vec<Map>("map");
327 const vector<Map> sqltypes = conf.Vec<Map>("sql-type");
328 const vector<string> _ignore = conf.Vec<string>("ignore");
329 const vector<string> primary = conf.Vec<string>("primary");
330
331 // -------------------------------------------------------------------------
332
333 if (verbose>0)
334 cout << "\n-------------------------- Evaluating file -------------------------" << endl;
335
336 TFile f(file.c_str());
337 if (f.IsZombie())
338 {
339 cerr << "Could not open file " << file << endl;
340 return 1;
341 }
342
343 if (verbose>0)
344 cout << "File: " << file << endl;
345
346 TTree *T = 0;
347 f.GetObject(tree.c_str(), T);
348 if (!T)
349 {
350 cerr << "Could not open tree " << tree << endl;
351 return 2;
352 }
353
354 if (verbose>0)
355 cout << "Tree: " << tree << endl;
356
357 T->SetMakeClass(1);
358
359 TObjArray *branches = T->GetListOfBranches();
360 TObjArray *leaves = T->GetListOfLeaves();
361
362 if (print_branches)
363 {
364 cout << '\n';
365 branches->Print();
366 }
367
368 if (verbose>0)
369 cout << T->GetEntriesFast() << " events found." << endl;
370
371
372 if (verbose>0)
373 cout << branches->GetEntries() << " branches found." << endl;
374
375 if (print_leaves)
376 {
377 cout << '\n';
378 leaves->Print();
379 }
380 if (verbose>0)
381 cout << leaves->GetEntries() << " leaves found." << endl;
382
383 string query =
384 "CREATE TABLE IF NOT EXISTS `"+table+"`\n"
385 "(\n";
386
387 vector<Container> vec;
388
389
390 TIter Next(leaves);
391 TObject *o = 0;
392 while ((o=Next()))
393 {
394 TLeaf *L = T->GetLeaf(o->GetName());
395
396 if (verbose>2)
397 cout << '\n' << o->GetName() << " [" << L->GetTypeName() << "]";
398
399 string name = o->GetName();
400
401
402 bool found = false;
403 for (auto b=_ignore.cbegin(); b!=_ignore.cend(); b++)
404 {
405 if (boost::regex_match(name, boost::regex(*b)))
406 {
407 found = true;
408 if (verbose>2)
409 cout << " (-ignored-)";
410 break;
411 }
412 }
413 if (found)
414 continue;
415
416 const string tn = L->GetTypeName();
417
418 auto it = ConvRoot.find(tn);
419 if (it==ConvRoot.end())
420 {
421 if (verbose>2)
422 cout << " (-n/a-)";
423 continue;
424 }
425
426 if (verbose==2)
427 cout << '\n' << o->GetName() << " [" << L->GetTypeName() << "]";
428
429 for (auto m=mymap.cbegin(); m!=mymap.cend(); m++)
430 name = boost::regex_replace(name, boost::regex(m->first), m->second);
431
432 if (verbose>1)
433 cout << " (" << name << ")";
434
435 string sqltype = it->second.second;
436
437 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
438 if (m->first==name)
439 sqltype = m->second;
440
441 if (!vec.empty())
442 query += ",\n";
443 query += " `"+name+"` "+sqltype+" NOT NULL COMMENT '"+o->GetName()+"'";
444
445 vec.emplace_back(o->GetName(), name, it->second.first);
446 T->SetBranchAddress(o->GetName(), vec.back().ptr);
447 }
448
449 if (verbose>1)
450 cout << "\n\n";
451 if (verbose>0)
452 cout << vec.size() << " leaves setup for reading." << endl;
453
454 UInt_t datatype = 0;
455 const bool has_datatype = T->SetBranchAddress("DataType.fVal", &datatype) >= 0;
456
457 // Setiing up branch status (must be after all SetBranchAddress)
458 T->SetBranchStatus("*", 0);
459 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
460 T->SetBranchStatus(c->branch.c_str(), 1);
461
462 if (has_datatype)
463 {
464 T->SetBranchStatus("DataType.fVal", 1);
465 if (verbose>0)
466 cout << "Rows with DataType.fVal!=1 will be skipped." << endl;
467 }
468
469 // -------------------------------------------------------------------------
470 // Checking for database connection
471
472 Database connection(uri);
473
474 try
475 {
476 if (!force)
477 connection.connected();
478 }
479 catch (const exception &e)
480 {
481 cerr << "SQL connection failed: " << e.what() << '\n' << endl;
482 return 3;
483 }
484
485 if (verbose>0)
486 {
487 cout << "Client Version: " << connection.client_version() << '\n';
488 cout << "Server Version: " << connection.server_version() << endl;
489 }
490
491 if (print_connection)
492 {
493 try
494 {
495 const auto &res1 = connection.query("SHOW STATUS LIKE 'Compression'").store();
496 cout << "Compression of databse connection is " << string(res1[0][1]) << endl;
497
498 const auto &res2 = connection.query("SHOW STATUS LIKE 'Ssl_cipher'").store();
499 cout << "Connection to databases is " << (string(res2[0][1]).empty()?"UNENCRYPTED":"ENCRYPTED ("+string(res2[0][1])+")") << endl;
500 }
501 catch (const exception &e)
502 {
503 cerr << "\nSHOW STATUS LIKE COMPRESSION\n\n";
504 cerr << "SQL query failed:\n" << e.what() << endl;
505 return 6;
506 }
507 }
508
509 // -------------------------------------------------------------------------
510
511 if (verbose>0)
512 cout << "\n--------------------------- Database Table -------------------------" << endl;
513
514 if (!primary.empty())
515 query += ",\n PRIMARY KEY USING BTREE (`"+boost::algorithm::join(primary, "`, `")+"`)\n";
516
517 query +=
518 ")\n"
519 "DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci\n";
520 if (!engine.empty())
521 query += "ENGINE="+engine+"\n";
522 if (!row_format.empty())
523 query += "ROW_FORMAT="+row_format+"\n";
524 query += "COMMENT='created by "+conf.GetName()+"'\n";
525
526 // FIXME: Can we omit the catching to be able to print the
527 // query 'autmatically'?
528 try
529 {
530 if (drop)
531 {
532 // => Simple result
533 if (!dry_run)
534 connection.query("DROP TABLE `"+table+"`").execute();
535 if (verbose>0)
536 cout << "Table `" << table << "` dropped." << endl;
537 }
538 }
539 catch (const exception &e)
540 {
541 cerr << "DROP TABLE `" << table << "`\n\n";
542 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
543 return 4;
544 }
545
546 try
547 {
548 if (create && !dry_run)
549 connection.query(query).execute();
550 }
551 catch (const exception &e)
552 {
553 cerr << query << "\n\n";
554 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
555 return 5;
556 }
557
558 if (print_create)
559 cout << query << endl;
560
561 if (create && verbose>0)
562 cout << "Table `" << table << "` created." << endl;
563
564 // -------------------------------------------------------------------------
565
566 if (verbose>0)
567 cout << "\n---------------------------- Reading file --------------------------" << endl;
568
569 //query = update ? "UPDATE" : "INSERT";
570 query = "INSERT ";
571 if (ignore_errors)
572 query += "IGNORE ";
573 query += "`"+table+"`\n"
574 "(\n";
575
576 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
577 {
578 if (c!=vec.cbegin())
579 query += ",\n";
580 query += " `"+c->column+"`";
581 }
582
583 query +=
584 "\n)\n"
585 "VALUES\n";
586
587 size_t count = 0;
588
589 const size_t num = max>0 && (max-first)<T->GetEntriesFast() ? (max-first) : T->GetEntriesFast();
590 for (size_t j=first; j<num; j++)
591 {
592 T->GetEntry(j);
593 if (has_datatype && datatype!=1)
594 continue;
595
596 if (count>0)
597 query += ",\n";
598
599 query += "(\n";
600
601 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
602 {
603 if (c!=vec.cbegin())
604 query += ",\n";
605
606 query += " "+c->fmt();
607
608 if (print_insert)
609 query += " /* "+c->column+" -> "+c->branch+" */";
610 }
611 query += "\n)";
612
613 count ++;
614 }
615
616 if (!duplicate.empty())
617 query += "\nON DUPLICATE KEY UPDATE\n " + boost::join(duplicate, ",\n ");
618
619 if (verbose>0)
620 cout << count << " out of " << num << " row(s) read from file [N=" << first << ".." << num-1 << "]." << endl;
621
622 if (count==0)
623 {
624 if (verbose>0)
625 cout << "Total execution time: " << Time().UnixTime()-start.UnixTime() << "s\n" << endl;
626 return 0;
627 }
628
629 // -------------------------------------------------------------------------
630
631 if (verbose>0)
632 {
633 cout << "\n--------------------------- Inserting data -------------------------" << endl;
634 cout << "Sending INSERT query (" << query.length() << " bytes)" << endl;
635 }
636
637 try
638 {
639 if (!noinsert && !dry_run)
640 {
641 auto q = connection.query(query);
642 q.execute();
643 cout << q.info() << '\n' << endl;
644 }
645 else
646 cout << "Insert query skipped!" << endl;
647
648 if (print_insert)
649 cout << query << endl;
650 }
651 catch (const exception &e)
652 {
653 if (verbose>1 || query.length()<80*25)
654 cerr << query << "\n\n";
655 cerr << "SQL query failed (" << query.length() << " bytes):\n" << e.what() << '\n' << endl;
656 return 6;
657 }
658
659 if (verbose>0)
660 {
661 const auto sec = Time().UnixTime()-start.UnixTime();
662 cout << "Total execution time: " << sec << "s ";
663 cout << "(" << Tools::Fractional(sec/count) << "s/row)\n";
664
665 try
666 {
667 const auto resw =
668 connection.query("SHOW WARNINGS").store();
669
670 for (size_t i=0; i<resw.num_rows(); i++)
671 {
672 const mysqlpp::Row &roww = resw[i];
673
674 cout << roww["Level"] << '[' << roww["Code"] << "]: ";
675 cout << roww["Message"] << '\n';
676 }
677 cout << endl;
678
679 }
680 catch (const exception &e)
681 {
682 cerr << "\nSHOW WARNINGS\n\n";
683 cerr << "SQL query failed:\n" << e.what() << '\n' <<endl;
684 return 7;
685 }
686 }
687
688 if (print_connection)
689 {
690 try
691 {
692 // Exchange _send and _received as it is the view of the server
693 const auto &res1 = connection.query("SHOW STATUS LIKE 'Bytes_%'").store();
694 cout << left << setw(16) << res1[1]["Variable_name"] << ' ' << Tools::Scientific(res1[0]["Value"]) << endl;
695 cout << left << setw(16) << res1[0]["Variable_name"] << ' ' << Tools::Scientific(res1[1]["Value"]) << endl;
696 cout << endl;
697 }
698 catch (const exception &e)
699 {
700 cerr << "\nSHOW STATUS LIKE 'Bytes_%'\n\n";
701 cerr << "SQL query failed:\n" << e.what() << endl;
702 return 6;
703 }
704 }
705
706 return 0;
707}
Note: See TracBrowser for help on using the repository browser.