FACT++  1.0
rootifysql.cc
Go to the documentation of this file.
1 #include "Database.h"
2 
3 #include "Time.h"
4 #include "Configuration.h"
5 
6 #include <TROOT.h>
7 #include <TSystem.h>
8 #include <TFile.h>
9 #include <TTree.h>
10 
11 using namespace std;
12 
13 // ------------------------------------------------------------------------
14 
16 {
17  po::options_description control("Rootify SQL");
18  control.add_options()
19  ("uri,u", var<string>()->required(), "Database link as in\n\tuser:password@server[:port]/database.")
20  ("query,q", var<string>(""), "MySQL query (overwrites --file)")
21  ("file", var<string>("rootify.sql"), "An ASCII file with the MySQL query (overwrites --query)")
22  ("ignore-null,i", po_switch(), "Do not skip rows containing any NULL field")
23  ("out,o", var<string>("rootify.root"), "Output root file name")
24  ("force,f", po_switch(), "Force overwriting an existing root file ('RECREATE')")
25  ("update", po_switch(), "Update an existing root file with the new tree ('UPDATE')")
26  ("compression,c", var<uint16_t>(1), "zlib compression level for the root file")
27  ("tree,t", var<string>("Result"), "Name of the root tree")
28  ("display,d", po_switch(), "Displays contents on the screen (most usefull in combination with mysql statements as SHOW or EXPLAIN)")
29  ("null,n", po_switch(), "Redirect the output file to /dev/null")
30  ("delimiter", var<string>(""), "The delimiter used if contents are displayed with --display (default=\\t)")
31  ("verbose,v", var<uint16_t>(1), "Verbosity (0: quiet, 1: default, 2: more, 3, ...)")
32  ;
33 
34  po::positional_options_description p;
35  p.add("file", 1); // The 1st positional options
36  p.add("out", 2); // The 2nd positional options
37 
38  conf.AddOptions(control);
39  conf.SetArgumentPositions(p);
40 }
41 
42 void PrintUsage()
43 {
44  cout <<
45  "rootifysql - Converts the result of a mysql query into a root file\n"
46  "\n"
47  "For convenience, this documentation uses the extended version of the options, "
48  "refer to the output below to get the abbreviations.\n"
49  "\n"
50  "Writes the result of a mysql query into a root file. For each column, a branch is "
51  "created of type double with the field name as name. This is usually the column name "
52  "if not specified otherwise by the AS mysql directive.\n"
53  "\n"
54  "Columns with CHAR or VARCHAR as field type are ignored. DATETIME, DATE and TIME "
55  "columns are converted to unix time (time_t). Rows containing any file which is "
56  "NULL are skipped if not suppressed by the --ignore-null option. Ideally, the query "
57  "is compiled in a way that no NULL field is returned. With the --display option the "
58  "result of the request is printed on the screen (NULL skipping still in action). "
59  "This can be useful to create an ascii file or to show results as 'SHOW DATABASES' "
60  "or 'EXPLAIN table'. To redirect the contents into an ascii file, the option -v0 "
61  "is useful. To suppredd writing to an output file --null can be used.\n"
62  "\n"
63  "The default is to read the query from a file called rootify.sql. Except if a different "
64  "filename is specified by the --file option or a query is given with --query.\n"
65  "\n"
66  "Comments in the query-file can be placed according to the SQL standard inline "
67  "/*comment*/ or on individual lines introduces with # or --.\n"
68  "\n"
69  "In case of succes, 0 is returned, a value>0 otherwise.\n"
70  "\n"
71  "Usage: rootifysql [rootify.sql [rootify.root]] [-u URI] [-q query|-f file] [-i] [-o out] [-f] [-cN] [-t tree] [-vN]\n"
72  "\n"
73  ;
74  cout << endl;
75 }
76 
77 int main(int argc, const char* argv[])
78 {
79  Time start;
80 
81  gROOT->SetBatch();
82 
83  Configuration conf(argv[0]);
85  SetupConfiguration(conf);
86 
87  if (!conf.DoParse(argc, argv))
88  return 127;
89 
90  // ----------------------------- Evaluate options --------------------------
91  const string uri = conf.Get<string>("uri");
92  const string out = conf.Get<string>("out");
93  const string file = conf.Get<string>("file");
94  const string tree = conf.Get<string>("tree");
95  const bool force = conf.Get<bool>("force");
96  const bool ignore = conf.Get<bool>("ignore-null");
97  const bool update = conf.Get<bool>("update");
98  const bool display = conf.Get<bool>("display");
99  const bool noout = conf.Get<bool>("null");
100  const uint16_t verbose = conf.Get<uint16_t>("verbose");
101  const uint16_t compression = conf.Get<uint16_t>("compression");
102  const string delimiter = conf.Get<string>("delimiter");
103  // -------------------------------------------------------------------------
104 
105  if (verbose>0)
106  cout << "\n--------------------- Rootify SQL ----------------------" << endl;
107 
108  string query = conf.Get<string>("query");
109  if (query.empty())
110  {
111  if (verbose>0)
112  cout << "Reading query from file '" << file << "'." << endl;
113 
114  ifstream fin(file);
115  if (!fin)
116  {
117  cerr << "Could not open '" << file << "': " << strerror(errno) << endl;
118  return 1;
119  }
120  getline(fin, query, (char)fin.eof());
121  }
122 
123  if (query.empty())
124  {
125  cerr << "No query specified." << endl;
126  return 2;
127  }
128 
129  // -------------------------- Check for file permssion ---------------------
130  // Strictly speaking, checking for write permission and existance is not necessary,
131  // but it is convenient that the user does not find out that it failed after
132  // waiting long for the query result
133  //
134  // I am using root here instead of boost to be
135  // consistent with the access pattern by TFile
136  TString path(noout?"/dev/null":out.c_str());
137  gSystem->ExpandPathName(path);
138 
139  if (!noout)
140  {
141  FileStat_t stat;
142  const Int_t exist = !gSystem->GetPathInfo(path, stat);
143  const Bool_t write = !gSystem->AccessPathName(path, kWritePermission) && R_ISREG(stat.fMode);
144 
145  if ((update && !exist) || (update && exist && !write) || (force && exist && !write))
146  {
147  cerr << "File '" << path << "' is not writable." << endl;
148  return 3;
149  }
150 
151  if (!update && !force && exist)
152  {
153  cerr << "File '" << path << "' already exists." << endl;
154  return 4;
155  }
156  }
157  // -------------------------------------------------------------------------
158 
159  if (query.back()!='\n')
160  query += '\n';
161 
162  if (verbose>2)
163  cout << '\n' << query << endl;
164 
165  if (verbose>0)
166  cout << "Requesting data..." << endl;
167 
168  Time start2;
169 
170  // -------------------------- Request data from database -------------------
171  const mysqlpp::StoreQueryResult res =
172  Database(uri).query(query).store();
173  // -------------------------------------------------------------------------
174 
175  if (verbose>0)
176  {
177  cout << res.size() << " rows received." << endl;
178  cout << "Query time: " << Time().UnixTime()-start2.UnixTime() << "s" << endl;
179  }
180 
181  if (res.empty())
182  {
183  cerr << "Nothing to write." << endl;
184  return 5;
185  }
186 
187  if (verbose>0)
188  cout << "Opening file '" << path << "' [compression=" << compression << "]..." << endl;
189 
190  // ----------------------------- Open output file --------------------------
191  TFile tfile(path, update?"UPDATE":(force?"RECREATE":"CREATE"), "Rootify SQL", compression);
192  if (tfile.IsZombie())
193  return 6;
194  // -------------------------------------------------------------------------
195 
196  const mysqlpp::Row &r = res.front();
197 
198  if (verbose>0)
199  cout << "Trying to setup " << r.size() << " branches..." << endl;
200 
201  if (verbose>1)
202  cout << endl;
203 
204  const mysqlpp::FieldNames &l = *r.field_list().list;
205 
206  vector<double> buf(l.size());
207  vector<uint8_t> typ(l.size(),'n');
208 
209  UInt_t cols = 0;
210 
211  // -------------------- Configure branches of TTree ------------------------
212  TTree *ttree = new TTree(tree.c_str(), query.c_str());
213  for (size_t i=0; i<l.size(); i++)
214  {
215  const string t = r[i].type().sql_name();
216 
217  if (t.find("DATETIME")!=string::npos)
218  typ[i] = 'd';
219  else
220  if (t.find("DATE")!=string::npos)
221  typ[i] = 'D';
222  else
223  if (t.find("TIME")!=string::npos)
224  typ[i] = 'T';
225  else
226  if (t.find("VARCHAR")!=string::npos)
227  typ[i] = 'V';
228  else
229  if (t.find("CHAR")!=string::npos)
230  typ[i] = 'C';
231 
232  const bool use = typ[i]!='V' && typ[i]!='C';
233 
234  if (verbose>1)
235  cout << (use?" + ":" - ") << l[i].c_str() << " [" << t << "] {" << typ[i] << "}\n";
236 
237  if (use)
238  {
239  ttree->Branch(l[i].c_str(), buf.data()+i);
240  cols++;
241  }
242  }
243  // -------------------------------------------------------------------------
244 
245  if (verbose>1)
246  cout << endl;
247  if (verbose>0)
248  cout << "Configured " << cols << " branches.\nFilling branches..." << endl;
249 
250  if (display)
251  {
252  cout << endl;
253  cout << "#";
254  for (size_t i=0; i<l.size(); i++)
255  cout << ' ' << l[i].c_str();
256  cout << endl;
257  }
258 
259  // ---------------------- Fill TTree with DB data --------------------------
260  size_t skip = 0;
261  for (auto row=res.begin(); row<res.end(); row++)
262  {
263  ostringstream sout;
264 
265  size_t idx=0;
266  for (auto col=row->begin(); col!=row->end(); col++, idx++)
267  {
268  if (display)
269  {
270  if (idx>0)
271  sout << (delimiter.empty()?"\t":delimiter);
272  sout << col->c_str();
273  }
274 
275  if (!ignore && col->is_null())
276  {
277  skip++;
278  break;
279  }
280 
281  switch (typ[idx])
282  {
283  case 'd':
284  buf[idx] = time_t((mysqlpp::DateTime)(*col));
285  break;
286 
287  case 'D':
288  buf[idx] = time_t((mysqlpp::Date)(*col));
289  break;
290 
291  case 'T':
292  buf[idx] = time_t((mysqlpp::Time)(*col));
293  break;
294 
295  case 'V':
296  case 'C':
297  break;
298 
299  default:
300  buf[idx] = atof(col->c_str());
301  }
302  }
303 
304  if (idx==row->size())
305  {
306  ttree->Fill();
307  if (display)
308  cout << sout.str() << endl;
309  }
310  }
311  // -------------------------------------------------------------------------
312 
313  if (display)
314  cout << '\n' << endl;
315 
316  if (verbose>0)
317  {
318  if (skip>0)
319  cout << skip << " rows skipped due to NULL field." << endl;
320 
321  cout << ttree->GetEntries() << " rows filled into tree." << endl;
322  }
323 
324  ttree->Write();
325  tfile.Close();
326 
327  if (verbose>0)
328  {
329  cout << "File closed.\n";
330  cout << "Execution time: " << Time().UnixTime()-start.UnixTime() << "s\n";
331  cout << "--------------------------------------------------------" << endl;
332  }
333 
334  return 0;
335 }
int start(int initState)
Definition: feeserver.c:1740
void SetupConfiguration(Configuration &conf)
Definition: rootifysql.cc:15
int i
Definition: db_dim_client.c:21
Adds some functionality to boost::posix_time::ptime for our needs.
Definition: Time.h:30
void SetPrintUsage(const std::function< void(void)> &func)
T Get(const std::string &var)
po::typed_value< bool > * po_switch()
STL namespace.
void SetArgumentPositions(const po::positional_options_description &desc)
void PrintUsage()
Definition: rootifysql.cc:42
Display * display
Definition: did.h:37
void AddOptions(const po::options_description &opt, bool visible=true)
Definition: Configuration.h:92
double UnixTime() const
Definition: Time.cc:195
int main(int argc, const char *argv[])
Definition: rootifysql.cc:77
Commandline parsing, resource file parsing and database access.
Definition: Configuration.h:9
TT t
Definition: test_client.c:26
static int verbose
Definition: t_erfa_c.c:4
bool DoParse(int argc, const char **argv, const std::function< void()> &func=std::function< void()>())