SQLsmith  v1.2.1-5-gfacd7a8
A random SQL query generator
sqlsmith.cc
1 #include "config.h"
2 
3 #include <iostream>
4 #include <chrono>
5 
6 #ifndef HAVE_BOOST_REGEX
7 #include <regex>
8 #else
9 #include <boost/regex.hpp>
10 using boost::regex;
11 using boost::smatch;
12 using boost::regex_match;
13 #endif
14 
15 #include <thread>
16 #include <typeinfo>
17 
18 #include "random.hh"
19 #include "grammar.hh"
20 #include "relmodel.hh"
21 #include "schema.hh"
22 #include "gitrev.h"
23 
24 #include "log.hh"
25 #include "dump.hh"
26 #include "impedance.hh"
27 #include "dut.hh"
28 
29 #ifdef HAVE_LIBSQLITE3
30 #include "sqlite.hh"
31 #endif
32 
33 #ifdef HAVE_MONETDB
34 #include "monetdb.hh"
35 #endif
36 
37 #include "postgres.hh"
38 
39 using namespace std;
40 
41 using namespace std::chrono;
42 
43 extern "C" {
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <signal.h>
47 }
48 
49 /* make the cerr logger globally accessible so we can emit one last
50  report on SIGINT */
51 cerr_logger *global_cerr_logger;
52 
53 extern "C" void cerr_log_handler(int)
54 {
55  if (global_cerr_logger)
56  global_cerr_logger->report();
57  exit(1);
58 }
59 
60 int main(int argc, char *argv[])
61 {
62  cerr << PACKAGE_NAME " " GITREV << endl;
63 
64  map<string,string> options;
65  regex optregex("--(help|log-to|verbose|target|sqlite|monetdb|version|dump-all-graphs|dump-all-queries|seed|dry-run|max-queries|rng-state|exclude-catalog)(?:=((?:.|\n)*))?");
66 
67  for(char **opt = argv+1 ;opt < argv+argc; opt++) {
68  smatch match;
69  string s(*opt);
70  if (regex_match(s, match, optregex)) {
71  options[string(match[1])] = match[2];
72  } else {
73  cerr << "Cannot parse option: " << *opt << endl;
74  options["help"] = "";
75  }
76  }
77 
78  if (options.count("help")) {
79  cerr <<
80  " --target=connstr postgres database to send queries to" << endl <<
81 #ifdef HAVE_LIBSQLITE3
82  " --sqlite=URI SQLite database to send queries to" << endl <<
83 #endif
84 #ifdef HAVE_MONETDB
85  " --monetdb=connstr MonetDB database to send queries to" <<endl <<
86 #endif
87  " --log-to=connstr log errors to postgres database" << endl <<
88  " --seed=int seed RNG with specified int instead of PID" << endl <<
89  " --dump-all-queries print queries as they are generated" << endl <<
90  " --dump-all-graphs dump generated ASTs" << endl <<
91  " --dry-run print queries instead of executing them" << endl <<
92  " --exclude-catalog don't generate queries using catalog relations" << endl <<
93  " --max-queries=long terminate after generating this many queries" << endl <<
94  " --rng-state=string deserialize dumped rng state" << endl <<
95  " --verbose emit progress output" << endl <<
96  " --version print version information and exit" << endl <<
97  " --help print available command line options and exit" << endl;
98  return 0;
99  } else if (options.count("version")) {
100  return 0;
101  }
102 
103  try
104  {
105  shared_ptr<schema> schema;
106  if (options.count("sqlite")) {
107 #ifdef HAVE_LIBSQLITE3
108  schema = make_shared<schema_sqlite>(options["sqlite"], options.count("exclude-catalog"));
109 #else
110  cerr << "Sorry, " PACKAGE_NAME " was compiled without SQLite support." << endl;
111  return 1;
112 #endif
113  }
114  else if(options.count("monetdb")) {
115 #ifdef HAVE_MONETDB
116  schema = make_shared<schema_monetdb>(options["monetdb"]);
117 #else
118  cerr << "Sorry, " PACKAGE_NAME " was compiled without MonetDB support." << endl;
119  return 1;
120 #endif
121  }
122  else
123  schema = make_shared<schema_pqxx>(options["target"], options.count("exclude-catalog"));
124 
125  scope scope;
126  long queries_generated = 0;
127  schema->fill_scope(scope);
128 
129  if (options.count("rng-state")) {
130  istringstream(options["rng-state"]) >> smith::rng;
131  } else {
132  smith::rng.seed(options.count("seed") ? stoi(options["seed"]) : getpid());
133  }
134 
135  vector<shared_ptr<logger> > loggers;
136 
137  loggers.push_back(make_shared<impedance_feedback>());
138 
139  if (options.count("log-to"))
140  loggers.push_back(make_shared<pqxx_logger>(
141  options.count("sqlite") ? options["sqlite"] : options["target"],
142  options["log-to"], *schema));
143 
144  if (options.count("verbose")) {
145  auto l = make_shared<cerr_logger>();
146  global_cerr_logger = &*l;
147  loggers.push_back(l);
148  signal(SIGINT, cerr_log_handler);
149  }
150 
151  if (options.count("dump-all-graphs"))
152  loggers.push_back(make_shared<ast_logger>());
153 
154  if (options.count("dump-all-queries"))
155  loggers.push_back(make_shared<query_dumper>());
156 
157  if (options.count("dry-run")) {
158  while (1) {
159  shared_ptr<prod> gen = statement_factory(&scope);
160  gen->out(cout);
161  for (auto l : loggers)
162  l->generated(*gen);
163  cout << ";" << endl;
164  queries_generated++;
165 
166  if (options.count("max-queries")
167  && (queries_generated >= stol(options["max-queries"])))
168  return 0;
169  }
170  }
171 
172  shared_ptr<dut_base> dut;
173 
174  if (options.count("sqlite")) {
175 #ifdef HAVE_LIBSQLITE3
176  dut = make_shared<dut_sqlite>(options["sqlite"]);
177 #else
178  cerr << "Sorry, " PACKAGE_NAME " was compiled without SQLite support." << endl;
179  return 1;
180 #endif
181  }
182  else if(options.count("monetdb")) {
183 #ifdef HAVE_MONETDB
184  dut = make_shared<dut_monetdb>(options["monetdb"]);
185 #else
186  cerr << "Sorry, " PACKAGE_NAME " was compiled without MonetDB support." << endl;
187  return 1;
188 #endif
189  }
190  else
191  dut = make_shared<dut_libpq>(options["target"]);
192 
193  while (1) /* Loop to recover connection loss */
194  {
195  try {
196  while (1) { /* Main loop */
197 
198  if (options.count("max-queries")
199  && (++queries_generated > stol(options["max-queries"]))) {
200  if (global_cerr_logger)
201  global_cerr_logger->report();
202  return 0;
203  }
204 
205  /* Invoke top-level production to generate AST */
206  shared_ptr<prod> gen = statement_factory(&scope);
207 
208  for (auto l : loggers)
209  l->generated(*gen);
210 
211  /* Generate SQL from AST */
212  ostringstream s;
213  gen->out(s);
214 
215  /* Try to execute it */
216  try {
217  dut->test(s.str());
218  for (auto l : loggers)
219  l->executed(*gen);
220  } catch (const dut::failure &e) {
221  for (auto l : loggers)
222  try {
223  l->error(*gen, e);
224  } catch (runtime_error &e) {
225  cerr << endl << "log failed: " << typeid(*l).name() << ": "
226  << e.what() << endl;
227  }
228  if ((dynamic_cast<const dut::broken *>(&e))) {
229  /* re-throw to outer loop to recover session. */
230  throw;
231  }
232  }
233  }
234  }
235  catch (const dut::broken &e) {
236  /* Give server some time to recover. */
237  this_thread::sleep_for(milliseconds(1000));
238  }
239  }
240  }
241  catch (const exception &e) {
242  cerr << e.what() << endl;
243  return 1;
244  }
245 }
Dump syntax trees as GraphML.
Base class for device under test.
grammar: Top-level and unsorted grammar productions
feedback to the grammar about failed productions
logging
schema and dut classes for MonetDB
schema and dut classes for PostgreSQL
randomness
supporting classes for the grammar
Base class providing schema information to grammar.
schema and dut classes for SQLite 3
stderr logger
Definition: log.hh:41
Definition: schema.hh:16