SQLsmith  v1.2.1-5-gfacd7a8
A random SQL query generator
grammar.cc
1 #include <typeinfo>
2 #include <numeric>
3 #include <algorithm>
4 #include <stdexcept>
5 #include <cassert>
6 
7 #include "random.hh"
8 #include "relmodel.hh"
9 #include "grammar.hh"
10 #include "schema.hh"
11 #include "impedance.hh"
12 
13 using namespace std;
14 
15 shared_ptr<table_ref> table_ref::factory(prod *p) {
16  try {
17  if (p->level < 3 + d6()) {
18  if (d6() > 3 && p->level < d6())
19  return make_shared<table_subquery>(p);
20  if (d6() > 3)
21  return make_shared<joined_table>(p);
22  }
23  if (d6() > 3)
24  return make_shared<table_or_query_name>(p);
25  else
26  return make_shared<table_sample>(p);
27  } catch (runtime_error &e) {
28  p->retry();
29  }
30  return factory(p);
31 }
32 
33 table_or_query_name::table_or_query_name(prod *p) : table_ref(p) {
34  t = random_pick(scope->tables);
35  refs.push_back(make_shared<aliased_relation>(scope->stmt_uid("ref"), t));
36 }
37 
38 void table_or_query_name::out(std::ostream &out) {
39  out << t->ident() << " as " << refs[0]->ident();
40 }
41 
42 target_table::target_table(prod *p, table *victim) : table_ref(p)
43 {
44  while (! victim
45  || victim->schema == "pg_catalog"
46  || !victim->is_base_table
47  || !victim->columns().size()) {
48  struct named_relation *pick = random_pick(scope->tables);
49  victim = dynamic_cast<table *>(pick);
50  retry();
51  }
52  victim_ = victim;
53  refs.push_back(make_shared<aliased_relation>(scope->stmt_uid("target"), victim));
54 }
55 
56 void target_table::out(std::ostream &out) {
57  out << victim_->ident() << " as " << refs[0]->ident();
58 }
59 
60 table_sample::table_sample(prod *p) : table_ref(p) {
61  match();
62  retry_limit = 1000; /* retries are cheap here */
63  do {
64  auto pick = random_pick(scope->schema->base_tables);
65  t = dynamic_cast<struct table*>(pick);
66  retry();
67  } while (!t || !t->is_base_table);
68 
69  refs.push_back(make_shared<aliased_relation>(scope->stmt_uid("sample"), t));
70  percent = 0.1 * d100();
71  method = (d6() > 2) ? "system" : "bernoulli";
72 }
73 
74 void table_sample::out(std::ostream &out) {
75  out << t->ident() <<
76  " as " << refs[0]->ident() <<
77  " tablesample " << method <<
78  " (" << percent << ") ";
79 }
80 
81 table_subquery::table_subquery(prod *p, bool lateral)
82  : table_ref(p), is_lateral(lateral) {
83  query = make_shared<query_spec>(this, scope, lateral);
84  string alias = scope->stmt_uid("subq");
85  relation *aliased_rel = &query->select_list->derived_table;
86  refs.push_back(make_shared<aliased_relation>(alias, aliased_rel));
87 }
88 
89 table_subquery::~table_subquery() { }
90 
92  query->accept(v);
93  v->visit(this);
94 }
95 
96 shared_ptr<join_cond> join_cond::factory(prod *p, table_ref &lhs, table_ref &rhs)
97 {
98  try {
99  if (d6() < 6)
100  return make_shared<expr_join_cond>(p, lhs, rhs);
101  else
102  return make_shared<simple_join_cond>(p, lhs, rhs);
103  } catch (runtime_error &e) {
104  p->retry();
105  }
106  return factory(p, lhs, rhs);
107 }
108 
109 simple_join_cond::simple_join_cond(prod *p, table_ref &lhs, table_ref &rhs)
110  : join_cond(p, lhs, rhs)
111 {
112 retry:
113  named_relation *left_rel = &*random_pick(lhs.refs);
114 
115  if (!left_rel->columns().size())
116  { retry(); goto retry; }
117 
118  named_relation *right_rel = &*random_pick(rhs.refs);
119 
120  column &c1 = random_pick(left_rel->columns());
121 
122  for (auto c2 : right_rel->columns()) {
123  if (c1.type == c2.type) {
124  condition +=
125  left_rel->ident() + "." + c1.name + " = " + right_rel->ident() + "." + c2.name + " ";
126  break;
127  }
128  }
129  if (condition == "") {
130  retry(); goto retry;
131  }
132 }
133 
134 void simple_join_cond::out(std::ostream &out) {
135  out << condition;
136 }
137 
138 expr_join_cond::expr_join_cond(prod *p, table_ref &lhs, table_ref &rhs)
139  : join_cond(p, lhs, rhs), joinscope(p->scope)
140 {
141  scope = &joinscope;
142  for (auto ref: lhs.refs)
143  joinscope.refs.push_back(&*ref);
144  for (auto ref: rhs.refs)
145  joinscope.refs.push_back(&*ref);
146  search = bool_expr::factory(this);
147 }
148 
149 void expr_join_cond::out(std::ostream &out) {
150  out << *search;
151 }
152 
153 joined_table::joined_table(prod *p) : table_ref(p) {
154  lhs = table_ref::factory(this);
155  rhs = table_ref::factory(this);
156 
157  condition = join_cond::factory(this, *lhs, *rhs);
158 
159  if (d6()<4) {
160  type = "inner";
161  } else if (d6()<4) {
162  type = "left";
163  } else {
164  type = "right";
165  }
166 
167  for (auto ref: lhs->refs)
168  refs.push_back(ref);
169  for (auto ref: rhs->refs)
170  refs.push_back(ref);
171 }
172 
173 void joined_table::out(std::ostream &out) {
174  out << *lhs;
175  indent(out);
176  out << type << " join " << *rhs;
177  indent(out);
178  out << "on (" << *condition << ")";
179 }
180 
181 void table_subquery::out(std::ostream &out) {
182  if (is_lateral)
183  out << "lateral ";
184  out << "(" << *query << ") as " << refs[0]->ident();
185 }
186 
187 void from_clause::out(std::ostream &out) {
188  if (! reflist.size())
189  return;
190  out << "from ";
191 
192  for (auto r = reflist.begin(); r < reflist.end(); r++) {
193  indent(out);
194  out << **r;
195  if (r + 1 != reflist.end())
196  out << ",";
197  }
198 }
199 
200 from_clause::from_clause(prod *p) : prod(p) {
201  reflist.push_back(table_ref::factory(this));
202  for (auto r : reflist.back()->refs)
203  scope->refs.push_back(&*r);
204 
205  while (d6() > 5) {
206  // add a lateral subquery
207  if (!impedance::matched(typeid(lateral_subquery)))
208  break;
209  reflist.push_back(make_shared<lateral_subquery>(this));
210  for (auto r : reflist.back()->refs)
211  scope->refs.push_back(&*r);
212  }
213 }
214 
215 select_list::select_list(prod *p) : prod(p)
216 {
217  do {
218  shared_ptr<value_expr> e = value_expr::factory(this);
219  value_exprs.push_back(e);
220  ostringstream name;
221  name << "c" << columns++;
222  sqltype *t=e->type;
223  assert(t);
224  derived_table.columns().push_back(column(name.str(), t));
225  } while (d6() > 1);
226 }
227 
228 void select_list::out(std::ostream &out)
229 {
230  int i = 0;
231  for (auto expr = value_exprs.begin(); expr != value_exprs.end(); expr++) {
232  indent(out);
233  out << **expr << " as " << derived_table.columns()[i].name;
234  i++;
235  if (expr+1 != value_exprs.end())
236  out << ", ";
237  }
238 }
239 
240 void query_spec::out(std::ostream &out) {
241  out << "select " << set_quantifier << " "
242  << *select_list;
243  indent(out);
244  out << *from_clause;
245  indent(out);
246  out << "where ";
247  out << *search;
248  if (limit_clause.length()) {
249  indent(out);
250  out << limit_clause;
251  }
252 }
253 
255  virtual void visit(prod *p) {
256  if (dynamic_cast<window_function*>(p))
257  throw("window function");
258  joined_table* join = dynamic_cast<joined_table*>(p);
259  if (join && join->type != "inner")
260  throw("outer join");
261  query_spec* subquery = dynamic_cast<query_spec*>(p);
262  if (subquery)
263  subquery->set_quantifier = "";
264  table_or_query_name* tab = dynamic_cast<table_or_query_name*>(p);
265  if (tab) {
266  table *actual_table = dynamic_cast<table*>(tab->t);
267  if (actual_table && !actual_table->is_insertable)
268  throw("read only");
269  if (actual_table->name.find("pg_"))
270  throw("catalog");
271  }
272  table_sample* sample = dynamic_cast<table_sample*>(p);
273  if (sample) {
274  table *actual_table = dynamic_cast<table*>(sample->t);
275  if (actual_table && !actual_table->is_insertable)
276  throw("read only");
277  if (actual_table->name.find("pg_"))
278  throw("catalog");
279  }
280  } ;
281 };
282 
283 
284 select_for_update::select_for_update(prod *p, struct scope *s, bool lateral)
285  : query_spec(p,s,lateral)
286 {
287  static const char *modes[] = {
288  "update",
289  "share",
290  "no key update",
291  "key share",
292  };
293 
294  try {
296  this->accept(&v1);
297 
298  } catch (const char* reason) {
299  lockmode = 0;
300  return;
301  }
302  lockmode = modes[d6()%(sizeof(modes)/sizeof(*modes))];
303  set_quantifier = ""; // disallow distinct
304 }
305 
306 void select_for_update::out(std::ostream &out) {
308  if (lockmode) {
309  indent(out);
310  out << " for " << lockmode;
311  }
312 }
313 
314 query_spec::query_spec(prod *p, struct scope *s, bool lateral) :
315  prod(p), myscope(s)
316 {
317  scope = &myscope;
318  scope->tables = s->tables;
319 
320  if (lateral)
321  scope->refs = s->refs;
322 
323  from_clause = make_shared<struct from_clause>(this);
324  select_list = make_shared<struct select_list>(this);
325 
326  set_quantifier = (d100() == 1) ? "distinct" : "";
327 
328  search = bool_expr::factory(this);
329 
330  if (d6() > 2) {
331  ostringstream cons;
332  cons << "limit " << d100() + d100();
333  limit_clause = cons.str();
334  }
335 }
336 
337 long prepare_stmt::seq;
338 
339 void modifying_stmt::pick_victim()
340 {
341  do {
342  struct named_relation *pick = random_pick(scope->tables);
343  victim = dynamic_cast<struct table*>(pick);
344  retry();
345  } while (! victim
346  || victim->schema == "pg_catalog"
347  || !victim->is_base_table
348  || !victim->columns().size());
349 }
350 
351 modifying_stmt::modifying_stmt(prod *p, struct scope *s, table *victim)
352  : prod(p), myscope(s)
353 {
354  scope = &myscope;
355  scope->tables = s->tables;
356 
357  if (!victim)
358  pick_victim();
359 }
360 
361 
362 delete_stmt::delete_stmt(prod *p, struct scope *s, table *v)
363  : modifying_stmt(p,s,v) {
364  scope->refs.push_back(victim);
365  search = bool_expr::factory(this);
366 }
367 
368 delete_returning::delete_returning(prod *p, struct scope *s, table *victim)
369  : delete_stmt(p, s, victim) {
370  match();
371  select_list = make_shared<struct select_list>(this);
372 }
373 
374 insert_stmt::insert_stmt(prod *p, struct scope *s, table *v)
375  : modifying_stmt(p, s, v)
376 {
377  match();
378 
379  for (auto col : victim->columns()) {
380  auto expr = value_expr::factory(this, col.type);
381  assert(expr->type == col.type);
382  value_exprs.push_back(expr);
383  }
384 }
385 
386 void insert_stmt::out(std::ostream &out)
387 {
388  out << "insert into " << victim->ident() << " ";
389 
390  if (!value_exprs.size()) {
391  out << "default values";
392  return;
393  }
394 
395  out << "values (";
396 
397  for (auto expr = value_exprs.begin();
398  expr != value_exprs.end();
399  expr++) {
400  indent(out);
401  out << **expr;
402  if (expr+1 != value_exprs.end())
403  out << ", ";
404  }
405  out << ")";
406 }
407 
408 set_list::set_list(prod *p, table *target) : prod(p)
409 {
410  do {
411  for (auto col : target->columns()) {
412  if (d6() < 4)
413  continue;
414  auto expr = value_expr::factory(this, col.type);
415  value_exprs.push_back(expr);
416  names.push_back(col.name);
417  }
418  } while (!names.size());
419 }
420 
421 void set_list::out(std::ostream &out)
422 {
423  assert(names.size());
424  out << " set ";
425  for (size_t i = 0; i < names.size(); i++) {
426  indent(out);
427  out << names[i] << " = " << *value_exprs[i];
428  if (i+1 != names.size())
429  out << ", ";
430  }
431 }
432 
433 update_stmt::update_stmt(prod *p, struct scope *s, table *v)
434  : modifying_stmt(p, s, v) {
435  scope->refs.push_back(victim);
436  search = bool_expr::factory(this);
437  set_list = make_shared<struct set_list>(this, victim);
438 }
439 
440 void update_stmt::out(std::ostream &out)
441 {
442  out << "update " << victim->ident() << *set_list;
443 }
444 
445 update_returning::update_returning(prod *p, struct scope *s, table *v)
446  : update_stmt(p, s, v) {
447  match();
448 
449  select_list = make_shared<struct select_list>(this);
450 }
451 
452 
453 upsert_stmt::upsert_stmt(prod *p, struct scope *s, table *v)
454  : insert_stmt(p,s,v)
455 {
456  match();
457 
458  if (!victim->constraints.size())
459  fail("need table w/ constraint for upsert");
460 
461  set_list = std::make_shared<struct set_list>(this, victim);
462  search = bool_expr::factory(this);
463  constraint = random_pick(victim->constraints);
464 }
465 
466 shared_ptr<prod> statement_factory(struct scope *s)
467 {
468  try {
469  s->new_stmt();
470  if (d42() == 1)
471  return make_shared<merge_stmt>((struct prod *)0, s);
472  if (d42() == 1)
473  return make_shared<insert_stmt>((struct prod *)0, s);
474  else if (d42() == 1)
475  return make_shared<delete_returning>((struct prod *)0, s);
476  else if (d42() == 1) {
477  return make_shared<upsert_stmt>((struct prod *)0, s);
478  } else if (d42() == 1)
479  return make_shared<update_returning>((struct prod *)0, s);
480  else if (d6() > 4)
481  return make_shared<select_for_update>((struct prod *)0, s);
482  else if (d6() > 5)
483  return make_shared<common_table_expression>((struct prod *)0, s);
484  return make_shared<query_spec>((struct prod *)0, s);
485  } catch (runtime_error &e) {
486  return statement_factory(s);
487  }
488 }
489 
491 {
492  v->visit(this);
493  for(auto q : with_queries)
494  q->accept(v);
495  query->accept(v);
496 }
497 
498 common_table_expression::common_table_expression(prod *parent, struct scope *s)
499  : prod(parent), myscope(s)
500 {
501  scope = &myscope;
502  do {
503  shared_ptr<query_spec> query = make_shared<query_spec>(this, s);
504  with_queries.push_back(query);
505  string alias = scope->stmt_uid("jennifer");
506  relation *relation = &query->select_list->derived_table;
507  auto aliased_rel = make_shared<aliased_relation>(alias, relation);
508  refs.push_back(aliased_rel);
509  scope->tables.push_back(&*aliased_rel);
510 
511  } while (d6() > 2);
512 
513  retry:
514  do {
515  auto pick = random_pick(s->tables);
516  scope->tables.push_back(pick);
517  } while (d6() > 3);
518  try {
519  query = make_shared<query_spec>(this, scope);
520  } catch (runtime_error &e) {
521  retry();
522  goto retry;
523  }
524 
525 }
526 
527 void common_table_expression::out(std::ostream &out)
528 {
529  out << "WITH " ;
530  for (size_t i = 0; i < with_queries.size(); i++) {
531  indent(out);
532  out << refs[i]->ident() << " AS " << "(" << *with_queries[i] << ")";
533  if (i+1 != with_queries.size())
534  out << ", ";
535  indent(out);
536  }
537  out << *query;
538  indent(out);
539 }
540 
541 merge_stmt::merge_stmt(prod *p, struct scope *s, table *v)
542  : modifying_stmt(p,s,v) {
543  match();
544  target_table_ = make_shared<target_table>(this, victim);
545  data_source = table_ref::factory(this);
546 // join_condition = join_cond::factory(this, *target_table_, *data_source);
547  join_condition = make_shared<simple_join_cond>(this, *target_table_, *data_source);
548 
549 
550  /* Put data_source into scope but not target_table. Visibility of
551  the latter varies depending on kind of when clause. */
552 // for (auto r : data_source->refs)
553 // scope->refs.push_back(&*r);
554 
555  clauselist.push_back(when_clause::factory(this));
556  while (d6()>4)
557  clauselist.push_back(when_clause::factory(this));
558 }
559 
560 void merge_stmt::out(std::ostream &out)
561 {
562  out << "MERGE INTO " << *target_table_;
563  indent(out);
564  out << "USING " << *data_source;
565  indent(out);
566  out << "ON " << *join_condition;
567  indent(out);
568  for (auto p : clauselist) {
569  out << *p;
570  indent(out);
571  }
572 }
573 
575 {
576  v->visit(this);
577  target_table_->accept(v);
578  data_source->accept(v);
579  join_condition->accept(v);
580  for (auto p : clauselist)
581  p->accept(v);
582 
583 }
584 
585 when_clause::when_clause(merge_stmt *p)
586  : prod(p)
587 {
588  condition = bool_expr::factory(this);
589  matched = d6() > 3;
590 }
591 
592 void when_clause::out(std::ostream &out)
593 {
594  out << (matched ? "WHEN MATCHED " : "WHEN NOT MATCHED");
595  indent(out);
596  out << "AND " << *condition;
597  indent(out);
598  out << " THEN ";
599  out << (matched ? "DELETE" : "DO NOTHING");
600 }
601 
603 {
604  v->visit(this);
605  condition->accept(v);
606 }
607 
608 when_clause_update::when_clause_update(merge_stmt *p)
609  : when_clause(p), myscope(p->scope)
610 {
611  myscope.tables = scope->tables;
612  myscope.refs = scope->refs;
613  scope = &myscope;
614  scope->refs.push_back(&*(p->target_table_->refs[0]));
615 
616  set_list = std::make_shared<struct set_list>(this, p->victim);
617 }
618 
619 void when_clause_update::out(std::ostream &out) {
620  out << "WHEN MATCHED AND " << *condition;
621  indent(out);
622  out << " THEN UPDATE " << *set_list;
623 }
624 
626 {
627  v->visit(this);
628  set_list->accept(v);
629 }
630 
631 
632 when_clause_insert::when_clause_insert(struct merge_stmt *p)
633  : when_clause(p)
634 {
635  for (auto col : p->victim->columns()) {
636  auto expr = value_expr::factory(this, col.type);
637  assert(expr->type == col.type);
638  exprs.push_back(expr);
639  }
640 }
641 
642 void when_clause_insert::out(std::ostream &out) {
643  out << "WHEN NOT MATCHED AND " << *condition;
644  indent(out);
645  out << " THEN INSERT VALUES ( ";
646 
647  for (auto expr = exprs.begin();
648  expr != exprs.end();
649  expr++) {
650  out << **expr;
651  if (expr+1 != exprs.end())
652  out << ", ";
653  }
654  out << ")";
655 
656 }
657 
659 {
660  v->visit(this);
661  for (auto p : exprs)
662  p->accept(v);
663 }
664 
665 shared_ptr<when_clause> when_clause::factory(struct merge_stmt *p)
666 {
667  try {
668  switch(d6()) {
669  case 1:
670  case 2:
671  return make_shared<when_clause_insert>(p);
672  case 3:
673  case 4:
674  return make_shared<when_clause_update>(p);
675  default:
676  return make_shared<when_clause>(p);
677  }
678  } catch (runtime_error &e) {
679  p->retry();
680  }
681  return factory(p);
682 }
683 
grammar: Top-level and unsorted grammar productions
feedback to the grammar about failed productions
randomness
supporting classes for the grammar
Base class providing schema information to grammar.
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:527
virtual void accept(prod_visitor *v)
Visitor pattern for walking the AST.
Definition: grammar.cc:490
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:149
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:187
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:386
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:173
virtual void accept(prod_visitor *v)
Visitor pattern for walking the AST.
Definition: grammar.cc:574
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:560
Base class for walking the AST.
Definition: prod.hh:11
Base class for AST nodes.
Definition: prod.hh:17
virtual void indent(std::ostream &out)
Newline and indent according to tree level.
Definition: prod.cc:20
void retry()
Increase the retry count and throw an exception when retry_limit is exceeded.
Definition: prod.cc:27
long retry_limit
Maximum number of retries allowed before reporting a failure to the Parent prod.
Definition: prod.hh:30
int level
Level of this production in the AST. 0 for root node.
Definition: prod.hh:24
virtual void fail(const char *reason)
Report a "failed to generate" error.
Definition: prod.cc:44
virtual void match()
Check with the impedance matching code whether this production has been blacklisted and throw an exce...
Definition: prod.cc:38
virtual void accept(prod_visitor *v)
Visitor pattern for walking the AST.
Definition: prod.hh:41
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:240
void new_stmt()
Reset unique identifier counters.
Definition: relmodel.hh:112
vector< named_relation * > tables
available to table_ref productions
Definition: relmodel.hh:82
vector< named_relation * > refs
available to column_ref productions
Definition: relmodel.hh:84
string stmt_uid(const char *prefix)
Generate unique identifier with prefix.
Definition: relmodel.hh:105
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:306
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:228
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:421
virtual void accept(prod_visitor *v)
Visitor pattern for walking the AST.
Definition: grammar.hh:223
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:134
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:38
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:74
virtual void accept(prod_visitor *v)
Visitor pattern for walking the AST.
Definition: grammar.cc:91
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:181
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:56
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:440
virtual void accept(prod_visitor *v)
Visitor pattern for walking the AST.
Definition: grammar.cc:658
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:642
virtual void accept(prod_visitor *v)
Visitor pattern for walking the AST.
Definition: grammar.cc:625
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:619
virtual void out(std::ostream &out)
Emit SQL for this production.
Definition: grammar.cc:592
virtual void accept(prod_visitor *v)
Visitor pattern for walking the AST.
Definition: grammar.cc:602