source: trunk/FACT++/src/fits2sql.cc@ 19164

Last change on this file since 19164 was 19162, checked in by tbretz, 6 years ago
Updated returns.
File size: 31.0 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 "zfits.h"
11
12using namespace std;
13
14// ------------------------------------------------------------------------
15
16struct Map : pair<string, string>
17{
18 Map() { }
19};
20
21std::istream &operator>>(std::istream &in, Map &m)
22{
23 const istreambuf_iterator<char> eos;
24 string txt(istreambuf_iterator<char>(in), eos);
25
26 const boost::regex expr("((.*)[^\\\\])/(.*)");
27 boost::smatch match;
28 if (!boost::regex_match(txt, match, expr))
29 throw runtime_error("Could not evaluate map argument: "+txt);
30
31 m.first = match[1].str();
32 m.second = match[3].str();
33
34 return in;
35}
36
37
38void SetupConfiguration(Configuration &conf)
39{
40 po::options_description control("Root to SQL");
41 control.add_options()
42 ("uri,u", var<string>()
43#if BOOST_VERSION >= 104200
44 ->required()
45#endif
46 , "Database link as in\n\tuser:password@server[:port]/database[?compress=0|1].")
47 ("file", var<string>("")
48#if BOOST_VERSION >= 104200
49 ->required()
50#endif
51 , "The root file to read from")
52 ("create", po_switch(), "Create the database if not existing")
53 ("drop", po_switch(), "Drop the table (implies create)")
54 ("extension,e", var<string>(""), "Name of the fits extension (table) to convert")
55 ("table", var<string>(""), "Name of the table to use (default is the tree name)")
56 ("map", vars<Map>(), "A regular expression which is applied to the leaf name befoee it is used as SQL column name)")
57 ("sql-type", vars<Map>(), "Allows to overwrite the calculated SQL type for a given column e.g. 'sql-column-name/UNSIGNED IN'")
58 ("ignore", vars<string>(), "Ignore the given leaf, if the given regular expression matches")
59 ("unsigned", vars<string>(), "In fits files per default columns are signed. This interpretss the column as unsigned value. Use the FITS column name.")
60 ("primary", vars<string>(), "List of columns to be used as primary keys during table creation (in connection with --create)")
61 ("first", var<size_t>(size_t(0)), "First event to start with (default: 0), mainly for test purpose")
62 ("max", var<size_t>(size_t(0)), "Maximum number of events to process (0: all), mainly for test purpose")
63 ("engine", var<string>(""), "Database engine to be used when a new table is created")
64 ("row-format", var<string>(""), "Defines the ROW_FORMAT keyword for table creation")
65 ("duplicate", vars<string>(), "Specifies an assignment_list for an 'ON DUPLICATE KEY UPDATE' expression")
66 ("ignore-errors", po_switch(), "Adds the IGNORE keyword to the INSERT query (turns errors into warnings, ignores rows with errors)")
67 ("const.*", var<string>(), "Insert a constant number into the given column (--const.mycolumn=5). A special case is `/..../N/`")
68 ("conditional", po_switch(), "Conditional insert. Only insert if no entry exists yet with the constants defined by --const")
69 ("delete", po_switch(), "Delete all entries first which fit all constant columns defined by --const")
70 ("index", po_switch(), "If a table is created, all const columns are used as a single index")
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-extensions", po_switch(), "Print extensions (tables) from fits file")
78 ("print-connection", po_switch(), "Print database connection information")
79 ("print-columns", po_switch(), "Print columns in fits table")
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 ("print-select", po_switch(), "Print the SELECT query for the conditional execution")
83 ("print-delete", po_switch(), "Print the DELETE query")
84 ("verbose,v", var<uint16_t>(1), "Verbosity (0: quiet, 1: default, 2: more, 3, ...)")
85 ;
86
87 po::positional_options_description p;
88 p.add("file", 1); // The 1st positional options (n=1)
89
90 conf.AddOptions(control);
91 conf.AddOptions(debug);
92 conf.SetArgumentPositions(p);
93}
94
95void PrintUsage()
96{
97 cout <<
98 "fits2sql - Fills the data from a fits file into a database\n"
99 "\n"
100 "For convenience, this documentation uses the extended version of the options, "
101 "refer to the output below to get the abbreviations.\n"
102 "\n"
103 "This is a general purpose tool to fill the contents of a fite file into a database.\n"
104 "\n"
105 "Each fits file contians several table, so called extenbsions. Each tables has "
106 "a number of columns compilsed from basic data types. The default extension to "
107 "read from is the first one on the file but the name can be overwritten using "
108 "--extension. The default table name to fill the data into is identical to "
109 "the extension name. It can be overwritten using --table.\n"
110 "\n"
111 "The name of each column to which data is filled is obtained from "
112 "the fits column names. The column names can be checked using --print-columns. "
113 "Sometimes these names might not be convenient. To allow to simplify or replace "
114 "column names, regular expressions (using boost's regex) can be defined to change "
115 "the names. Note that these regular expressions are applied one by one on each "
116 "columns's name. A valid expression could "
117 "be:\n"
118 " --map=MHillas\\.f/\n"
119 "which would remove all occurances of 'MHillas.f'. This option can be used more than "
120 "once. They are applied in sequence. A single match does not stop the sequence.\n"
121 "\n"
122 "Sometimes it might also be convenient to skip a column. This can be done with "
123 "the --ignore resource. If the given regular expresion yields a match, the "
124 "column will be ignored. Note that the regular expression works on the raw-name "
125 "of the column not the readily mapped SQL column names. Example:\n"
126 " --ignore=ThetaSq\\..*\n"
127 "will skip all leaved which start with 'ThetaSq.'. This option can be used"
128 "more than once.\n"
129 "\n"
130 "The data type of each column is kept as close as possible to the columns' data "
131 "types. If for some reason this is not wanted, the data type of the SQL column "
132 "can be overwritten with --sql-type sql-column/sql-ytpe, for example:\n"
133 " --sql-type=FileId/UNSIGNED INT\n"
134 "while the first argument of the name of the SQL column to which the data type "
135 "should be applied. The second column is the basic SQL data type. The option can "
136 "be given more than once.\n"
137 "\n"
138 "Database interaction:\n"
139 "\n"
140 "To drop an existing table, --drop can be used.\n"
141 "\n"
142 "To create a table according to theSQL column names and data types, --create "
143 "can be used. The query used can be printed with --print-create even --create "
144 "has not been specified.\n"
145 "\n"
146 "To choose the columns which should become primary keys, use --primary, "
147 "for example:\n"
148 " --primary=col1\n"
149 "To define more than one column as primary key, the option can be given more than "
150 "once. Note that the combination of these columns must be unique.\n"
151 "\n"
152 "All columns are created as NOT NULL as default. To force a database engine "
153 "and/or a storage format, use --engine and --rot-format.\n"
154 "\n"
155 "Usually, the INSERT query would fail if the PRIMARY key exists already. "
156 "This can be avoided using the 'ON DUPLICATE KEY UPDATE' directive. With the "
157 "--duplicate, you can specify what should be updated in case of a duplicate key. "
158 "To keep the row untouched, you can just update the primary key "
159 "with the identical primary key, e.g. --duplicate='MyPrimary=VALUES(MyPrimary)'. "
160 "The --duplicate resource can be specified more than once to add more expressions "
161 "to the assignment_list. For more details, see the MySQL manual.\n"
162 "\n"
163 "Another possibility is to add the IGNORE keyword to the INSERT query by "
164 "--ignore-errors, which essentially ignores all errors and turns them into "
165 "warnings which are printed after the query succeeded.\n"
166 "\n"
167 "For debugging purpose, or to just create or drop a table, the final insert "
168 "query can be skipped using --no-insert. Note that for performance reason, "
169 "all data is collected in memory and a single INSERT query is issued at the "
170 "end.\n"
171 "\n"
172 "Using a higher verbosity level (-v), an overview of the written columns or all "
173 "processed leaves is printed depending on the verbosity level. The output looks "
174 "like the following\n"
175 " Leaf name [root data type] (SQL name)\n"
176 "for example\n"
177 " MTime.fTime.fMilliSec [Long64_t] (MilliSec)\n"
178 "which means that the leaf MTime.fTime.fMilliSec is detected to be a Long64_t "
179 "which is filled into a column called MilliSec. Leaves with non basic data types "
180 "are ignored automatically and are marked as (-n/a-). User ignored columnd "
181 "are marked as (-ignored-).\n"
182 "\n"
183 "A constant value for the given file can be inserted by using the --const directive. "
184 "For example --const.mycolumn=42 would insert 42 into a column called mycolumn. "
185 "The column is created as INT UNSIGNED as default which can be altered by "
186 "--sql-type. A special case is a value of the form `/regex/N/`. Here, the given "
187 "regular expression is applied to the filename and N specifies the N-th "
188 "sub-sequence which matches. To debug what matches, verbosity can be set to 3.\n"
189 "\n"
190 "Usually the previously defined constant values are helpful to create an index "
191 "which relates unambiguously the inserted data to the file. It might be useful "
192 "to delete all data which belongs to this particular file before new data is "
193 "entered. This can be achieved with the `--delete` directive. It deletes all "
194 "data from the table before inserting new data which fulfills the condition "
195 "defined by the `--const` directives.\n"
196 "\n"
197 "The constant values can also be used for a conditional execution (--conditional). "
198 "If any row with the given constant values are found, the execution is stopped "
199 "(note that this happend after the table drop/create but before the delete/insert.\n"
200 "\n"
201 "To ensure efficient access for a conditonal execution, it makes sense to have "
202 "an index created for those columns. This can be done during table creation "
203 "with the --index option.\n"
204 "\n"
205 "If a query failed, the query is printed to stderr together with the error message. "
206 "For the main INSERT query, this is only true if the verbosity level is at least 2 "
207 "or the query has less than 80*25 bytes.\n"
208 "\n"
209 "In case of success, 0 is returned, a value>0 otherwise.\n"
210 "\n"
211 "Usage: fits2sql [options] --uri URI fitsfile.fits[.gz]\n"
212 "\n"
213 ;
214 cout << endl;
215}
216
217enum BasicType_t
218{
219 kNone = 0,
220 kConst,
221 kVarchar,
222 kBool,
223 kFloat,
224 kDouble,
225 kInt8,
226 kUInt8,
227 kInt16,
228 kUInt16,
229 kInt32,
230 kUInt32,
231 kInt64,
232 kUInt64,
233// kMJD,
234};
235
236static const map<char, pair<BasicType_t, string>> ConvFits =
237{
238 { 'A', { kVarchar, "VARCHAR" } },
239 { 'a', { kVarchar, "VARCHAR" } },
240 { 'L', { kBool, "BOOLEAN" } },
241 { 'l', { kBool, "BOOLEAN" } },
242 { 'B', { kInt8, "TINYINT" } },
243 { 'b', { kUInt8, "TINYINT UNSIGNED" } },
244 { 'I', { kInt16, "SMALLINT" } },
245 { 'i', { kUInt16, "SMALLINT UNSIGNED" } },
246 { 'J', { kInt32, "INT" } },
247 { 'j', { kUInt32, "INT UNSIGNED" } },
248 { 'K', { kInt64, "BIGINT" } },
249 { 'k', { kUInt64, "BIGINT UNSIGNED" } },
250 { 'E', { kFloat, "FLOAT" } },
251 { 'e', { kFloat, "FLOAT" } },
252 { 'D', { kDouble, "DOUBLE" } },
253 { 'd', { kDouble, "DOUBLE" } },
254};
255
256struct Container
257{
258 string branch; // fits column name
259 string column; // sql column name
260 BasicType_t type;
261 size_t num;
262// double offset;
263 void *ptr;
264
265 Container(const string &b, const string &c, const BasicType_t &t, const size_t &n/*, const double &offset=0*/) : branch(b), column(c), type(t), num(n), ptr(0)
266 {
267 }
268 Container(const string &c, const string &value) : branch(value), column(c), type(kConst), num(1), ptr(0)
269 {
270 }
271 ~Container()
272 {
273 }
274
275 string fmt(const size_t &index) const
276 {
277 ostringstream str;
278
279 switch (type)
280 {
281 //case kVarchar: str << string(reinterpret_cast<char*>(ptr), num); break;
282 case kVarchar: str << string(reinterpret_cast<char*>(ptr), num).c_str(); break;
283 case kFloat: str << setprecision(8) << reinterpret_cast<float*>(ptr)[index]; break;
284// case kMJD: str << setprecision(16) << reinterpret_cast<double*>(ptr)[0]+offset; break;
285 case kDouble: str << setprecision(16) << reinterpret_cast<double*>(ptr)[index]; break;
286 case kBool:
287 case kInt8: str << int32_t(reinterpret_cast<int8_t*>(ptr)[index]); break;
288 case kUInt8: str << uint32_t(reinterpret_cast<uint8_t*>(ptr)[index]); break;
289 case kInt16: str << reinterpret_cast<int16_t*>(ptr)[index]; break;
290 case kUInt16: str << reinterpret_cast<uint16_t*>(ptr)[index]; break;
291 case kInt32: str << reinterpret_cast<int32_t*>(ptr)[index]; break;
292 case kUInt32: str << reinterpret_cast<uint32_t*>(ptr)[index]; break;
293 case kInt64: str << reinterpret_cast<int64_t*>(ptr)[index]; break;
294 case kUInt64: str << reinterpret_cast<uint64_t*>(ptr)[index]; break;
295 case kConst: str << branch; break;
296 case kNone:
297 break;
298 }
299
300 return str.str();
301 }
302};
303
304int main(int argc, const char* argv[])
305{
306 Time start;
307
308 Configuration conf(argv[0]);
309 conf.SetPrintUsage(PrintUsage);
310 SetupConfiguration(conf);
311
312 if (!conf.DoParse(argc, argv))
313 return 127;
314
315 // ----------------------------- Evaluate options --------------------------
316 const string uri = conf.Get<string>("uri");
317 const string file = conf.Get<string>("file");
318 const string extension = conf.Get<string>("extension");
319 string table = conf.Get<string>("table");
320
321 const uint16_t verbose = conf.Get<uint16_t>("verbose");
322 const size_t first = conf.Get<size_t>("first");
323 const size_t max = conf.Get<size_t>("max");
324
325 const bool drop = conf.Get<bool>("drop");
326 const bool create = conf.Get<bool>("create") || drop;
327 const bool noinsert = conf.Get<bool>("no-insert");
328 const bool dry_run = conf.Get<bool>("dry-run");
329 const bool conditional = conf.Get<bool>("conditional");
330 const bool run_delete = conf.Get<bool>("delete");
331 const bool index = conf.Get<bool>("index");
332
333 const string engine = conf.Get<string>("engine");
334 const string row_format = conf.Get<string>("row-format");
335
336 const bool ignore_errors = conf.Get<bool>("ignore-errors");
337
338 const bool print_connection = conf.Get<bool>("print-connection");
339 const bool print_extensions = conf.Get<bool>("print-extensions");
340 const bool print_columns = conf.Get<bool>("print-columns");
341 const bool print_create = conf.Get<bool>("print-create");
342 const bool print_insert = conf.Get<bool>("print-insert");
343 const bool print_select = conf.Get<bool>("print-select");
344 const bool print_delete = conf.Get<bool>("print-delete");
345
346 const vector<Map> mymap = conf.Vec<Map>("map");
347 const vector<Map> sqltypes = conf.Vec<Map>("sql-type");
348 const vector<string> _ignore = conf.Vec<string>("ignore");
349 const vector<string> primary = conf.Vec<string>("primary");
350
351 const vector<string> duplicate = conf.Vec<string>("duplicate");
352 const vector<string> notsigned = conf.Vec<string>("unsigned");
353
354 if (first>=max)
355 cerr << "WARNING: Resource `first` (" << first << ") exceeds `max` (" << max << ")" << endl;
356
357
358 // -------------------------------------------------------------------------
359
360 if (verbose>0)
361 cout << "\n-------------------------- Evaluating file -------------------------" << endl;
362
363 zfits f(file.c_str(), extension.c_str());
364 if (!f)
365 {
366 cerr << "Could not open file " << file << ": " << strerror(errno) << endl;
367 return 1;
368 }
369
370 if (verbose>0)
371 cout << "File: " << file << endl;
372
373 if (!extension.empty() && extension!=f.Get<string>("EXTNAME"))
374 {
375 cerr << "Extension " << extension << " not found in file." << endl;
376 return 2;
377 }
378
379 if (print_extensions)
380 {
381 cout << "\nTables:\n - " << boost::join(f.GetTables(), "\n - ") << '\n' << endl;
382 return 3;
383 }
384
385 if (verbose>0)
386 cout << "FITS extension [table]: " << f.Get<string>("EXTNAME") << endl;
387
388 if (table.empty())
389 table = f.Get<string>("EXTNAME");
390
391 if (verbose>0)
392 cout << "SQL table: " << table << endl;
393
394// const double mjdref = f.Get("MJDREF", double(0));
395
396 if (print_columns)
397 {
398 cout << '\n';
399 f.PrintColumns();
400 cout << '\n';
401 }
402
403 const auto cols = f.GetColumns();
404
405 if (verbose>0)
406 {
407 cout << f.GetNumRows() << " events found." << endl;
408 cout << cols.size() << " columns found." << endl;
409 }
410
411 string query =
412 "CREATE TABLE IF NOT EXISTS `"+table+"`\n"
413 "(\n";
414
415 vector<Container> vec;
416
417 const auto fixed = conf.GetWildcardOptions("const.*");
418
419 string where;
420 vector<string> vindex;
421 for (auto it=fixed.cbegin(); it!=fixed.cend(); it++)
422 {
423 const string name = it->substr(6);
424 string val = conf.Get<string>(*it);
425
426 boost::smatch match;
427 if (boost::regex_match(val, match, boost::regex("\\/(.+)\\/([0-9]*)\\/")))
428 {
429 string s = match[1];
430 size_t r = atoi(match[2].str().c_str());
431
432 if (boost::regex_search(file, match, boost::regex(s)))
433 {
434 if (verbose>2)
435 for (size_t i=0; i<match.size(); i++)
436 cout << "Regex match " << setw(2) << i << ": `" << match[i] << "`" << endl;
437
438 val = match[r];
439 }
440 }
441
442 if (verbose==2)
443 cout << "\n" << val << " [-const-]";
444 if (verbose>1)
445 cout << " (" << name << ")";
446
447 string sqltype = "INT UNSIGNED";
448
449 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
450 if (m->first==name)
451 sqltype = m->second;
452
453 if (!vec.empty())
454 query += ",\n";
455 query += " `"+name+"` "+sqltype+" NOT NULL COMMENT '--user--'";
456
457 vec.emplace_back(name, val);
458 where += " AND `"+name+"`="+val;
459 vindex.emplace_back(name);
460 }
461
462 const size_t nvec = vec.size();
463
464 for (auto ic=cols.cbegin(); ic!=cols.cend(); ic++)
465 {
466 const auto &col = ic->second;
467
468 if (verbose>2)
469 cout << '\n' << col.type << " " << ic->first << "[" << col.num << "]";
470
471 string name = ic->first;
472
473 bool found = false;
474 for (auto b=_ignore.cbegin(); b!=_ignore.cend(); b++)
475 {
476 if (boost::regex_match(name, boost::regex(*b)))
477 {
478 found = true;
479 if (verbose>2)
480 cout << " (-ignored-)";
481 break;
482 }
483 }
484 if (found)
485 continue;
486
487 const char tn = find(notsigned.cbegin(), notsigned.cend(), ic->first)!=notsigned.cend() ?
488 tolower(col.type) : toupper(col.type);
489
490 auto it = ConvFits.find(tn);
491 if (it==ConvFits.end())
492 {
493 if (verbose>2)
494 cout << " (-n/a-)";
495 continue;
496 }
497
498 if (verbose==2)
499 cout << '\n' << name << " [" << tn << "]";
500
501 for (auto m=mymap.cbegin(); m!=mymap.cend(); m++)
502 name = boost::regex_replace(name, boost::regex(m->first), m->second);
503
504 if (verbose>1)
505 cout << " (" << name << ")";
506
507 string sqltype = it->second.second;
508
509 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
510 if (m->first==name)
511 sqltype = m->second;
512
513 if (!vec.empty())
514 query += ",\n";
515
516 const size_t N = col.type=='A' ? 1 : col.num;
517 for (size_t i=0; i<N; i++)
518 {
519 query += " `"+name;
520 if (N>1)
521 query += "["+to_string(i)+"]";
522 query += "` "+sqltype;
523 if (col.type=='A')
524 query += '('+to_string(col.num)+')';
525 query += " NOT NULL COMMENT '"+ic->first;
526 if (!col.unit.empty())
527 query += "["+col.unit+"]";
528 if (!col.comment.empty())
529 query += ": "+col.comment;
530 query += +"'";
531 if (N>1 && i!=N-1)
532 query += ",\n";
533 }
534
535 const BasicType_t bt =
536 /*ic.first=="Time" && col.num==1 && col.unit=="MJD" && it->second.first==kDouble ?
537 kMJD :*/ it->second.first;
538
539 vec.emplace_back(ic->first, name, bt, col.num/*, mjdref*/);
540 vec.back().ptr = f.SetPtrAddress(ic->first);
541 }
542
543 if (verbose>1)
544 cout << "\n\n";
545 if (verbose>0)
546 {
547 if (nvec>0)
548 cout << nvec << " constant value column(s) configured." << endl;
549 cout << vec.size()-nvec << " FITS columns setup for reading." << endl;
550 }
551
552 // -------------------------------------------------------------------------
553 // Checking for database connection
554 if (verbose>0)
555 cout << "\n---------------------- Connecting database -------------------------" << endl;
556
557 Database connection(uri);
558
559 if (verbose>0)
560 {
561 cout << "Client Version: " << connection.client_version() << '\n';
562 cout << "Server Version: " << connection.server_version() << endl;
563 }
564
565 if (print_connection)
566 {
567 try
568 {
569 const auto &res1 = connection.query("SHOW STATUS LIKE 'Compression'").store();
570 cout << "Compression of databse connection is " << string(res1[0][1]) << endl;
571
572 const auto &res2 = connection.query("SHOW STATUS LIKE 'Ssl_cipher'").store();
573 cout << "Connection to databases is " << (string(res2[0][1]).empty()?"UNENCRYPTED":"ENCRYPTED ("+string(res2[0][1])+")") << endl;
574 }
575 catch (const exception &e)
576 {
577 cerr << "\nSHOW STATUS LIKE COMPRESSION\n\n";
578 cerr << "SQL query failed:\n" << e.what() << endl;
579 return 4;
580 }
581 }
582
583 // -------------------------------------------------------------------------
584
585 if (verbose>0)
586 cout << "\n--------------------------- Database Table -------------------------" << endl;
587
588 if (!primary.empty())
589 query += ",\n PRIMARY KEY USING BTREE (`"+boost::algorithm::join(primary, "`, `")+"`)";
590
591 if (!vindex.empty() && index)
592 query += ",\n INDEX USING BTREE (`"+boost::algorithm::join(vindex, "`, `")+"`)";
593
594 query +=
595 "\n)\n"
596 "DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci\n";
597 if (!engine.empty())
598 query += "ENGINE="+engine+"\n";
599 if (!row_format.empty())
600 query += "ROW_FORMAT="+row_format+"\n";
601 query += "COMMENT='created by "+conf.GetName()+"'\n";
602
603
604 // FIXME: Can we omit the catching to be able to print the
605 // query 'autmatically'?
606 try
607 {
608 if (drop)
609 {
610 // => Simple result
611 if (!dry_run)
612 connection.query("DROP TABLE `"+table+"`").execute();
613 if (verbose>0)
614 cout << "Table `" << table << "` dropped." << endl;
615 }
616 }
617 catch (const exception &e)
618 {
619 cerr << "DROP TABLE `" << table << "`\n\n";
620 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
621 return 5;
622 }
623
624 try
625 {
626 if (create && !dry_run)
627 connection.query(query).execute();
628 }
629 catch (const exception &e)
630 {
631 cerr << query << "\n\n";
632 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
633 return 6;
634 }
635
636 if (print_create)
637 cout << query << endl;
638
639 if (create && verbose>0)
640 cout << "Table `" << table << "` created." << endl;
641
642
643 try
644 {
645 if (conditional && !fixed.empty() && !drop)
646 {
647 const mysqlpp::StoreQueryResult res =
648 connection.query("SELECT 1 FROM `"+table+"` WHERE 1"+where+" LIMIT 1").store();
649
650 if (print_select)
651 cout << "SELECT 1 FROM `"+table+"` WHERE 1" << where << " LIMIT 1" << endl;
652
653 if (res.num_rows()>0)
654 {
655 if (verbose>0)
656 {
657 cout << "Conditional execution... detected existing rows!\n";
658 cout << "Exit.\n" << endl;
659 }
660 return 0;
661 }
662 }
663 }
664 catch (const exception &e)
665 {
666 cerr << "SELECT 1 FROM `"+table+"` WHERE 1" << where << " LIMIT 1\n\n";
667 cerr << "SQL query failed: " << e.what() << endl;
668 return 7;
669 }
670
671 try
672 {
673 if (run_delete && !fixed.empty() && !drop && !dry_run)
674 {
675 const mysqlpp::SimpleResult res =
676 connection.query("DELETE FROM `"+table+"` WHERE 1"+where).execute();
677
678 if (verbose>0)
679 cout << res.rows() << " row(s) deleted.\n" << endl;
680 }
681 }
682 catch (const exception &e)
683 {
684 cerr << "DELETE FROM `"+table+"` WHERE 1" << where << "\n\n";
685 cerr << "SQL query failed: " << e.what() << endl;
686 return 8;
687 }
688
689 if (print_delete)
690 cout << "DELETE FROM `"+table+"` WHERE 1" << where << endl;
691
692 // -------------------------------------------------------------------------
693
694 if (verbose>0)
695 cout << "\n---------------------------- Reading file --------------------------" << endl;
696
697 //query = update ? "UPDATE" : "INSERT";
698 query = "INSERT ";
699 if (ignore_errors)
700 query += "IGNORE ";
701 query += "`"+table+"`\n"
702 "(\n";
703
704 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
705 {
706 if (c!=vec.cbegin())
707 query += ",\n";
708
709 const size_t N = c->type==kVarchar ? 1 : c->num;
710 for (size_t i=0; i<N; i++)
711 {
712 if (N==1)
713 query += " `"+c->column+"`";
714 else
715 query += " `"+c->column+"["+to_string(i)+"]`";
716
717 if (N>1 && i!=N-1)
718 query += ",\n";
719 }
720 }
721
722 query +=
723 "\n)\n"
724 "VALUES\n";
725
726 size_t count = 0;
727
728 const size_t num = max>first && (max-first)<f.GetNumRows() ? (max-first) : f.GetNumRows();
729 for (size_t j=first; j<num; j++)
730 {
731 f.GetRow(j);
732
733 if (count>0)
734 query += ",\n";
735
736 query += "(\n";
737
738 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
739 {
740 if (c!=vec.cbegin())
741 query += ",\n";
742
743 const size_t N = c->type==kVarchar ? 1 : c->num;
744 for (size_t i=0; i<N; i++)
745 {
746 if (c->type==kVarchar)
747 query += " '"+c->fmt(i)+"'";
748 else
749 query += " "+c->fmt(i);
750
751 if (print_insert && i==0)
752 query += " /* "+c->column+" -> "+c->branch+" */";
753
754 if (N>1 && i!=N-1)
755 query += ",\n";
756 }
757 }
758 query += "\n)";
759
760 count ++;
761 }
762
763 if (!duplicate.empty())
764 query += "\nON DUPLICATE KEY UPDATE\n " + boost::join(duplicate, ",\n ");
765
766 if (verbose>0)
767 cout << count << " out of " << num << " row(s) read from file [N=" << first << ".." << num-1 << "]." << endl;
768
769 if (count==0)
770 {
771 if (verbose>0)
772 cout << "Total execution time: " << Time().UnixTime()-start.UnixTime() << "s\n" << endl;
773 return 0;
774 }
775
776 // -------------------------------------------------------------------------
777
778 if (verbose>0)
779 {
780 cout << "\n--------------------------- Inserting data -------------------------" << endl;
781 cout << "Sending INSERT query (" << query.length() << " bytes)" << endl;
782 }
783
784 try
785 {
786 if (!noinsert && !dry_run)
787 {
788 auto q = connection.query(query);
789 q.execute();
790 cout << q.info() << '\n' << endl;
791 }
792 else
793 cout << "Insert query skipped!" << endl;
794
795 if (print_insert)
796 cout << query << endl;
797 }
798 catch (const exception &e)
799 {
800 if (verbose>1 || query.length()<80*25)
801 cerr << query << "\n\n";
802 cerr << "SQL query failed (" << query.length() << " bytes):\n" << e.what() << '\n' << endl;
803 return 9;
804 }
805
806 if (verbose>0)
807 {
808 const auto sec = Time().UnixTime()-start.UnixTime();
809 cout << "Total execution time: " << sec << "s ";
810 cout << "(" << Tools::Fractional(sec/count) << "s/row)\n";
811
812 try
813 {
814 const auto resw =
815 connection.query("SHOW WARNINGS").store();
816
817 for (size_t i=0; i<resw.num_rows(); i++)
818 {
819 const mysqlpp::Row &roww = resw[i];
820
821 cout << roww["Level"] << '[' << roww["Code"] << "]: ";
822 cout << roww["Message"] << '\n';
823 }
824 cout << endl;
825
826 }
827 catch (const exception &e)
828 {
829 cerr << "\nSHOW WARNINGS\n\n";
830 cerr << "SQL query failed:\n" << e.what() << '\n' <<endl;
831 return 10;
832 }
833 }
834
835 if (print_connection)
836 {
837 try
838 {
839 // Exchange _send and _received as it is the view of the server
840 const auto &res1 = connection.query("SHOW STATUS LIKE 'Bytes_%'").store();
841 cout << left << setw(16) << res1[1]["Variable_name"] << ' ' << Tools::Scientific(res1[0]["Value"]) << endl;
842 cout << left << setw(16) << res1[0]["Variable_name"] << ' ' << Tools::Scientific(res1[1]["Value"]) << endl;
843 cout << endl;
844 }
845 catch (const exception &e)
846 {
847 cerr << "\nSHOW STATUS LIKE 'Bytes_%'\n\n";
848 cerr << "SQL query failed:\n" << e.what() << endl;
849 return 11;
850 }
851 }
852
853 return 0;
854}
Note: See TracBrowser for help on using the repository browser.