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

Last change on this file since 19169 was 19167, checked in by tbretz, 7 years ago
Minor fixed.
File size: 31.3 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/format/`. Here, the given "
187 "regular expression is applied to the filename and it is newly formated with "
188 "the new format string. For details on how backreferences work, see for example "
189 "the man-page of the sed utility.\n"
190 "\n"
191 "Usually the previously defined constant values are helpful to create an index "
192 "which relates unambiguously the inserted data to the file. It might be useful "
193 "to delete all data which belongs to this particular file before new data is "
194 "entered. This can be achieved with the `--delete` directive. It deletes all "
195 "data from the table before inserting new data which fulfills the condition "
196 "defined by the `--const` directives.\n"
197 "\n"
198 "The constant values can also be used for a conditional execution (--conditional). "
199 "If any row with the given constant values are found, the execution is stopped "
200 "(note that this happend after the table drop/create but before the delete/insert.\n"
201 "\n"
202 "To ensure efficient access for a conditonal execution, it makes sense to have "
203 "an index created for those columns. This can be done during table creation "
204 "with the --index option.\n"
205 "\n"
206 "If a query failed, the query is printed to stderr together with the error message. "
207 "For the main INSERT query, this is only true if the verbosity level is at least 2 "
208 "or the query has less than 80*25 bytes.\n"
209 "\n"
210 "In case of success, 0 is returned, a value>0 otherwise.\n"
211 "\n"
212 "Usage: fits2sql [options] --uri URI fitsfile.fits[.gz]\n"
213 "\n"
214 ;
215 cout << endl;
216}
217
218enum BasicType_t
219{
220 kNone = 0,
221 kConst,
222 kVarchar,
223 kBool,
224 kFloat,
225 kDouble,
226 kInt8,
227 kUInt8,
228 kInt16,
229 kUInt16,
230 kInt32,
231 kUInt32,
232 kInt64,
233 kUInt64,
234// kMJD,
235};
236
237static const map<char, pair<BasicType_t, string>> ConvFits =
238{
239 { 'A', { kVarchar, "VARCHAR" } },
240 { 'a', { kVarchar, "VARCHAR" } },
241 { 'L', { kBool, "BOOLEAN" } },
242 { 'l', { kBool, "BOOLEAN" } },
243 { 'B', { kInt8, "TINYINT" } },
244 { 'b', { kUInt8, "TINYINT UNSIGNED" } },
245 { 'I', { kInt16, "SMALLINT" } },
246 { 'i', { kUInt16, "SMALLINT UNSIGNED" } },
247 { 'J', { kInt32, "INT" } },
248 { 'j', { kUInt32, "INT UNSIGNED" } },
249 { 'K', { kInt64, "BIGINT" } },
250 { 'k', { kUInt64, "BIGINT UNSIGNED" } },
251 { 'E', { kFloat, "FLOAT" } },
252 { 'e', { kFloat, "FLOAT" } },
253 { 'D', { kDouble, "DOUBLE" } },
254 { 'd', { kDouble, "DOUBLE" } },
255};
256
257struct Container
258{
259 string branch; // fits column name
260 string column; // sql column name
261 BasicType_t type;
262 size_t num;
263// double offset;
264 void *ptr;
265
266 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)
267 {
268 }
269 Container(const string &c, const string &value) : branch(value), column(c), type(kConst), num(1), ptr(0)
270 {
271 }
272 ~Container()
273 {
274 }
275
276 string fmt(const size_t &index) const
277 {
278 ostringstream str;
279
280 switch (type)
281 {
282 //case kVarchar: str << string(reinterpret_cast<char*>(ptr), num); break;
283 case kVarchar: str << string(reinterpret_cast<char*>(ptr), num).c_str(); break;
284 case kFloat: str << setprecision(8) << reinterpret_cast<float*>(ptr)[index]; break;
285// case kMJD: str << setprecision(16) << reinterpret_cast<double*>(ptr)[0]+offset; break;
286 case kDouble: str << setprecision(16) << reinterpret_cast<double*>(ptr)[index]; break;
287 case kBool:
288 case kInt8: str << int32_t(reinterpret_cast<int8_t*>(ptr)[index]); break;
289 case kUInt8: str << uint32_t(reinterpret_cast<uint8_t*>(ptr)[index]); break;
290 case kInt16: str << reinterpret_cast<int16_t*>(ptr)[index]; break;
291 case kUInt16: str << reinterpret_cast<uint16_t*>(ptr)[index]; break;
292 case kInt32: str << reinterpret_cast<int32_t*>(ptr)[index]; break;
293 case kUInt32: str << reinterpret_cast<uint32_t*>(ptr)[index]; break;
294 case kInt64: str << reinterpret_cast<int64_t*>(ptr)[index]; break;
295 case kUInt64: str << reinterpret_cast<uint64_t*>(ptr)[index]; break;
296 case kConst: str << branch; break;
297 case kNone:
298 break;
299 }
300
301 return str.str();
302 }
303};
304
305int main(int argc, const char* argv[])
306{
307 Time start;
308
309 Configuration conf(argv[0]);
310 conf.SetPrintUsage(PrintUsage);
311 SetupConfiguration(conf);
312
313 if (!conf.DoParse(argc, argv))
314 return 127;
315
316 // ----------------------------- Evaluate options --------------------------
317 const string uri = conf.Get<string>("uri");
318 const string file = conf.Get<string>("file");
319 const string extension = conf.Get<string>("extension");
320 string table = conf.Get<string>("table");
321
322 const uint16_t verbose = conf.Get<uint16_t>("verbose");
323 const size_t first = conf.Get<size_t>("first");
324 const size_t max = conf.Get<size_t>("max");
325
326 const bool drop = conf.Get<bool>("drop");
327 const bool create = conf.Get<bool>("create") || drop;
328 const bool noinsert = conf.Get<bool>("no-insert");
329 const bool dry_run = conf.Get<bool>("dry-run");
330 const bool conditional = conf.Get<bool>("conditional");
331 const bool run_delete = conf.Get<bool>("delete");
332 const bool index = conf.Get<bool>("index");
333
334 const string engine = conf.Get<string>("engine");
335 const string row_format = conf.Get<string>("row-format");
336
337 const bool ignore_errors = conf.Get<bool>("ignore-errors");
338
339 const bool print_connection = conf.Get<bool>("print-connection");
340 const bool print_extensions = conf.Get<bool>("print-extensions");
341 const bool print_columns = conf.Get<bool>("print-columns");
342 const bool print_create = conf.Get<bool>("print-create");
343 const bool print_insert = conf.Get<bool>("print-insert");
344 const bool print_select = conf.Get<bool>("print-select");
345 const bool print_delete = conf.Get<bool>("print-delete");
346
347 const vector<Map> mymap = conf.Vec<Map>("map");
348 const vector<Map> sqltypes = conf.Vec<Map>("sql-type");
349 const vector<string> _ignore = conf.Vec<string>("ignore");
350 const vector<string> primary = conf.Vec<string>("primary");
351
352 const vector<string> duplicate = conf.Vec<string>("duplicate");
353 const vector<string> notsigned = conf.Vec<string>("unsigned");
354
355 if (first>=max)
356 cerr << "WARNING: Resource `first` (" << first << ") exceeds `max` (" << max << ")" << endl;
357
358
359 // -------------------------------------------------------------------------
360
361 if (verbose>0)
362 cout << "\n-------------------------- Evaluating file -------------------------" << endl;
363
364 zfits f(file.c_str(), extension.c_str());
365 if (!f)
366 {
367 cerr << "Could not open file " << file << ": " << strerror(errno) << endl;
368 return 1;
369 }
370
371 if (verbose>0)
372 cout << "File: " << file << endl;
373
374 if (!extension.empty() && extension!=f.Get<string>("EXTNAME"))
375 {
376 cerr << "Extension " << extension << " not found in file." << endl;
377 return 2;
378 }
379
380 if (print_extensions)
381 {
382 cout << "\nTables:\n - " << boost::join(f.GetTables(), "\n - ") << '\n' << endl;
383 return 3;
384 }
385
386 if (verbose>0)
387 cout << "FITS extension [table]: " << f.Get<string>("EXTNAME") << endl;
388
389 if (table.empty())
390 table = f.Get<string>("EXTNAME");
391
392 if (verbose>0)
393 cout << "SQL table: " << table << endl;
394
395// const double mjdref = f.Get("MJDREF", double(0));
396
397 if (print_columns)
398 {
399 cout << '\n';
400 f.PrintColumns();
401 cout << '\n';
402 }
403
404 const auto cols = f.GetColumns();
405
406 if (verbose>0)
407 {
408 cout << f.GetNumRows() << " events found." << endl;
409 cout << cols.size() << " columns found." << endl;
410 }
411
412 string query =
413 "CREATE TABLE IF NOT EXISTS `"+table+"`\n"
414 "(\n";
415
416 vector<Container> vec;
417
418 const auto fixed = conf.GetWildcardOptions("const.*");
419
420 string where;
421 vector<string> vindex;
422 for (auto it=fixed.cbegin(); it!=fixed.cend(); it++)
423 {
424 const string name = it->substr(6);
425 string val = conf.Get<string>(*it);
426
427 boost::smatch match;
428 if (boost::regex_match(val, match, boost::regex("\\/(.+)(?<!\\\\)\\/(.*)(?<!\\\\)\\/")))
429 {
430 const string reg = match[1];
431 const string fmt = match[2];
432
433 val = boost::regex_replace(file, boost::regex(reg), fmt.empty()?"$0":fmt,
434 boost::regex_constants::format_default|boost::regex_constants::format_no_copy);
435
436 if (verbose>0)
437 {
438 cout << "Regular expression detected for constant column `" << *it << "`\n";
439 cout << "Filename converted with /" << reg << "/ to /" << fmt << "/\n";
440 cout << "Filename: " << file << '\n';
441 cout << "Result: " << val << endl;
442 }
443 }
444
445 if (verbose==2)
446 cout << "\n" << val << " [-const-]";
447 if (verbose>1)
448 cout << " (" << name << ")";
449
450 string sqltype = "INT UNSIGNED";
451
452 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
453 if (m->first==name)
454 sqltype = m->second;
455
456 if (!vec.empty())
457 query += ",\n";
458 query += " `"+name+"` "+sqltype+" NOT NULL COMMENT '--user--'";
459
460 vec.emplace_back(name, val);
461 where += " AND `"+name+"`="+val;
462 vindex.emplace_back(name);
463 }
464
465 const size_t nvec = vec.size();
466
467 for (auto ic=cols.cbegin(); ic!=cols.cend(); ic++)
468 {
469 const auto &col = ic->second;
470
471 if (verbose>2)
472 cout << '\n' << col.type << " " << ic->first << "[" << col.num << "]";
473
474 string name = ic->first;
475
476 bool found = false;
477 for (auto b=_ignore.cbegin(); b!=_ignore.cend(); b++)
478 {
479 if (boost::regex_match(name, boost::regex(*b)))
480 {
481 found = true;
482 if (verbose>2)
483 cout << " (-ignored-)";
484 break;
485 }
486 }
487 if (found)
488 continue;
489
490 const char tn = find(notsigned.cbegin(), notsigned.cend(), ic->first)!=notsigned.cend() ?
491 tolower(col.type) : toupper(col.type);
492
493 auto it = ConvFits.find(tn);
494 if (it==ConvFits.end())
495 {
496 if (verbose>2)
497 cout << " (-n/a-)";
498 continue;
499 }
500
501 if (verbose==2)
502 cout << '\n' << name << " [" << tn << "]";
503
504 for (auto m=mymap.cbegin(); m!=mymap.cend(); m++)
505 name = boost::regex_replace(name, boost::regex(m->first), m->second);
506
507 if (verbose>1)
508 cout << " (" << name << ")";
509
510 string sqltype = it->second.second;
511
512 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
513 if (m->first==name)
514 sqltype = m->second;
515
516 if (!vec.empty())
517 query += ",\n";
518
519 const size_t N = col.type=='A' ? 1 : col.num;
520 for (size_t i=0; i<N; i++)
521 {
522 query += " `"+name;
523 if (N>1)
524 query += "["+to_string(i)+"]";
525 query += "` "+sqltype;
526 if (col.type=='A')
527 query += '('+to_string(col.num)+')';
528 query += " NOT NULL COMMENT '"+ic->first;
529 if (!col.unit.empty())
530 query += "["+col.unit+"]";
531 if (!col.comment.empty())
532 query += ": "+col.comment;
533 query += +"'";
534 if (N>1 && i!=N-1)
535 query += ",\n";
536 }
537
538 const BasicType_t bt =
539 /*ic.first=="Time" && col.num==1 && col.unit=="MJD" && it->second.first==kDouble ?
540 kMJD :*/ it->second.first;
541
542 vec.emplace_back(ic->first, name, bt, col.num/*, mjdref*/);
543 vec.back().ptr = f.SetPtrAddress(ic->first);
544 }
545
546 if (verbose>1)
547 cout << "\n\n";
548 if (verbose>0)
549 {
550 if (nvec>0)
551 cout << nvec << " constant value column(s) configured." << endl;
552 cout << vec.size()-nvec << " FITS columns setup for reading." << endl;
553 }
554
555 // -------------------------------------------------------------------------
556 // Checking for database connection
557 if (verbose>0)
558 cout << "\n---------------------- Connecting database -------------------------" << endl;
559
560 Database connection(uri);
561
562 if (verbose>0)
563 {
564 cout << "Client Version: " << connection.client_version() << '\n';
565 cout << "Server Version: " << connection.server_version() << endl;
566 }
567
568 if (print_connection)
569 {
570 try
571 {
572 const auto &res1 = connection.query("SHOW STATUS LIKE 'Compression'").store();
573 cout << "Compression of databse connection is " << string(res1[0][1]) << endl;
574
575 const auto &res2 = connection.query("SHOW STATUS LIKE 'Ssl_cipher'").store();
576 cout << "Connection to databases is " << (string(res2[0][1]).empty()?"UNENCRYPTED":"ENCRYPTED ("+string(res2[0][1])+")") << endl;
577 }
578 catch (const exception &e)
579 {
580 cerr << "\nSHOW STATUS LIKE COMPRESSION\n\n";
581 cerr << "SQL query failed:\n" << e.what() << endl;
582 return 4;
583 }
584 }
585
586 // -------------------------------------------------------------------------
587
588 if (verbose>0)
589 cout << "\n--------------------------- Database Table -------------------------" << endl;
590
591 if (!primary.empty())
592 query += ",\n PRIMARY KEY USING BTREE (`"+boost::algorithm::join(primary, "`, `")+"`)";
593
594 if (!vindex.empty() && index)
595 query += ",\n INDEX USING BTREE (`"+boost::algorithm::join(vindex, "`, `")+"`)";
596
597 query +=
598 "\n)\n"
599 "DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci\n";
600 if (!engine.empty())
601 query += "ENGINE="+engine+"\n";
602 if (!row_format.empty())
603 query += "ROW_FORMAT="+row_format+"\n";
604 query += "COMMENT='created by "+conf.GetName()+"'\n";
605
606
607 // FIXME: Can we omit the catching to be able to print the
608 // query 'autmatically'?
609 try
610 {
611 if (drop)
612 {
613 // => Simple result
614 if (!dry_run)
615 connection.query("DROP TABLE `"+table+"`").execute();
616 if (verbose>0)
617 cout << "Table `" << table << "` dropped." << endl;
618 }
619 }
620 catch (const exception &e)
621 {
622 cerr << "DROP TABLE `" << table << "`\n\n";
623 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
624 return 5;
625 }
626
627 try
628 {
629 if (create && !dry_run)
630 connection.query(query).execute();
631 }
632 catch (const exception &e)
633 {
634 cerr << query << "\n\n";
635 cerr << "SQL query failed:\n" << e.what() << '\n' << endl;
636 return 6;
637 }
638
639 if (print_create)
640 cout << query << endl;
641
642 if (create && verbose>0)
643 cout << "Table `" << table << "` created." << endl;
644
645
646 try
647 {
648 if (conditional && !fixed.empty() && !drop)
649 {
650 const mysqlpp::StoreQueryResult res =
651 connection.query("SELECT 1 FROM `"+table+"` WHERE 1"+where+" LIMIT 1").store();
652
653 if (print_select)
654 cout << "SELECT 1 FROM `"+table+"` WHERE 1" << where << " LIMIT 1" << endl;
655
656 if (res.num_rows()>0)
657 {
658 if (verbose>0)
659 {
660 cout << "Conditional execution... detected existing rows!\n";
661 cout << "Exit.\n" << endl;
662 }
663 return 0;
664 }
665 }
666 }
667 catch (const exception &e)
668 {
669 cerr << "SELECT 1 FROM `"+table+"` WHERE 1" << where << " LIMIT 1\n\n";
670 cerr << "SQL query failed: " << e.what() << endl;
671 return 7;
672 }
673
674 try
675 {
676 if (run_delete && !fixed.empty() && !drop && !dry_run)
677 {
678 const mysqlpp::SimpleResult res =
679 connection.query("DELETE FROM `"+table+"` WHERE 1"+where).execute();
680
681 if (verbose>0)
682 cout << res.rows() << " row(s) deleted.\n" << endl;
683 }
684 }
685 catch (const exception &e)
686 {
687 cerr << "DELETE FROM `" << table << "` WHERE 1" << where << "\n\n";
688 cerr << "SQL query failed: " << e.what() << endl;
689 return 8;
690 }
691
692 if (print_delete)
693 cout << "DELETE FROM `" << table << "` WHERE 1" << where << endl;
694
695 // -------------------------------------------------------------------------
696
697 if (verbose>0)
698 cout << "\n---------------------------- Reading file --------------------------" << endl;
699
700 //query = update ? "UPDATE" : "INSERT";
701 query = "INSERT ";
702 if (ignore_errors)
703 query += "IGNORE ";
704 query += "`"+table+"`\n"
705 "(\n";
706
707 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
708 {
709 if (c!=vec.cbegin())
710 query += ",\n";
711
712 const size_t N = c->type==kVarchar ? 1 : c->num;
713 for (size_t i=0; i<N; i++)
714 {
715 if (N==1)
716 query += " `"+c->column+"`";
717 else
718 query += " `"+c->column+"["+to_string(i)+"]`";
719
720 if (N>1 && i!=N-1)
721 query += ",\n";
722 }
723 }
724
725 query +=
726 "\n)\n"
727 "VALUES\n";
728
729 size_t count = 0;
730
731 const size_t num = max>first && (max-first)<f.GetNumRows() ? (max-first) : f.GetNumRows();
732 for (size_t j=first; j<num; j++)
733 {
734 f.GetRow(j);
735
736 if (count>0)
737 query += ",\n";
738
739 query += "(\n";
740
741 for (auto c=vec.cbegin(); c!=vec.cend(); c++)
742 {
743 if (c!=vec.cbegin())
744 query += ",\n";
745
746 const size_t N = c->type==kVarchar ? 1 : c->num;
747 for (size_t i=0; i<N; i++)
748 {
749 if (c->type==kVarchar)
750 query += " '"+c->fmt(i)+"'";
751 else
752 query += " "+c->fmt(i);
753
754 if (print_insert && i==0)
755 query += " /* "+c->column+" -> "+c->branch+" */";
756
757 if (N>1 && i!=N-1)
758 query += ",\n";
759 }
760 }
761 query += "\n)";
762
763 count ++;
764 }
765
766 if (!duplicate.empty())
767 query += "\nON DUPLICATE KEY UPDATE\n " + boost::join(duplicate, ",\n ");
768
769 if (verbose>0)
770 cout << count << " out of " << num << " row(s) read from file [N=" << first << ".." << num-1 << "]." << endl;
771
772 if (count==0)
773 {
774 if (verbose>0)
775 cout << "Total execution time: " << Time().UnixTime()-start.UnixTime() << "s\n" << endl;
776 return 0;
777 }
778
779 // -------------------------------------------------------------------------
780
781 if (verbose>0)
782 {
783 cout << "\n--------------------------- Inserting data -------------------------" << endl;
784 cout << "Sending INSERT query (" << query.length() << " bytes)" << endl;
785 }
786
787 try
788 {
789 if (!noinsert && !dry_run)
790 {
791 auto q = connection.query(query);
792 q.execute();
793 cout << q.info() << '\n' << endl;
794 }
795 else
796 cout << "Insert query skipped!" << endl;
797
798 if (print_insert)
799 cout << query << endl;
800 }
801 catch (const exception &e)
802 {
803 if (verbose>1 || query.length()<80*25)
804 cerr << query << "\n\n";
805 cerr << "SQL query failed (" << query.length() << " bytes):\n" << e.what() << '\n' << endl;
806 return 9;
807 }
808
809 if (verbose>0)
810 {
811 const auto sec = Time().UnixTime()-start.UnixTime();
812 cout << "Total execution time: " << sec << "s ";
813 cout << "(" << Tools::Fractional(sec/count) << "s/row)\n";
814
815 try
816 {
817 const auto resw =
818 connection.query("SHOW WARNINGS").store();
819
820 for (size_t i=0; i<resw.num_rows(); i++)
821 {
822 const mysqlpp::Row &roww = resw[i];
823
824 cout << roww["Level"] << '[' << roww["Code"] << "]: ";
825 cout << roww["Message"] << '\n';
826 }
827 cout << endl;
828
829 }
830 catch (const exception &e)
831 {
832 cerr << "\nSHOW WARNINGS\n\n";
833 cerr << "SQL query failed:\n" << e.what() << '\n' <<endl;
834 return 10;
835 }
836 }
837
838 if (print_connection)
839 {
840 try
841 {
842 // Exchange _send and _received as it is the view of the server
843 const auto &res1 = connection.query("SHOW STATUS LIKE 'Bytes_%'").store();
844 cout << left << setw(16) << res1[1]["Variable_name"] << ' ' << Tools::Scientific(res1[0]["Value"]) << endl;
845 cout << left << setw(16) << res1[0]["Variable_name"] << ' ' << Tools::Scientific(res1[1]["Value"]) << endl;
846 cout << endl;
847 }
848 catch (const exception &e)
849 {
850 cerr << "\nSHOW STATUS LIKE 'Bytes_%'\n\n";
851 cerr << "SQL query failed:\n" << e.what() << endl;
852 return 11;
853 }
854 }
855
856 return 0;
857}
Note: See TracBrowser for help on using the repository browser.