SQLsmith  v1.2.1-5-gfacd7a8
A random SQL query generator
postgres.cc
1 #include "postgres.hh"
2 #include "config.h"
3 #include <iostream>
4 
5 #ifndef HAVE_BOOST_REGEX
6 #include <regex>
7 #else
8 #include <boost/regex.hpp>
9 using boost::regex;
10 using boost::smatch;
11 using boost::regex_match;
12 #endif
13 
14 using namespace std;
15 
16 static regex e_timeout("ERROR: canceling statement due to statement timeout(\n|.)*");
17 static regex e_syntax("ERROR: syntax error at or near(\n|.)*");
18 
20 {
21  pg_type *t = dynamic_cast<pg_type*>(rvalue);
22 
23  if (!t) {
24  cerr << "unknown type: " << rvalue->name << endl;
25  return false;
26  }
27 
28  switch(typtype_) {
29  case 'b': /* base type */
30  case 'c': /* composite type */
31  case 'd': /* domain */
32  case 'r': /* range */
33  case 'm': /* multirange */
34  case 'e': /* enum */
35  return this == t;
36 
37  case 'p': /* pseudo type */
38  if (name == "anyarray") {
39  return t->typelem_ != InvalidOid;
40  } else if (name == "anynonarray") {
41  return t->typelem_ == InvalidOid;
42  } else if(name == "anyenum") {
43  return t->typtype_ == 'e';
44  } else if (name == "any") {
45  return true;
46  } else if (name == "anyelement") {
47  return t->typelem_ == InvalidOid;
48  } else if (name == "anyrange") {
49  return t->typtype_ == 'r';
50  } else if (name == "record") {
51  return t->typtype_ == 'c';
52  } else if (name == "cstring") {
53  return this == t;
54  } else {
55  return false;
56  }
57 
58  default:
59  throw std::logic_error("unknown typtype");
60  }
61 }
62 
63 dut_pqxx::dut_pqxx(std::string conninfo)
64  : c(conninfo)
65 {
66  c.set_variable("statement_timeout", "'1s'");
67  c.set_variable("client_min_messages", "'ERROR'");
68  c.set_variable("application_name", "'" PACKAGE "::dut'");
69 }
70 
71 void dut_pqxx::test(const std::string &stmt)
72 {
73  try {
74  if(!c.is_open())
75  c.activate();
76 
77  pqxx::work w(c);
78  w.exec(stmt.c_str());
79  w.abort();
80  } catch (const pqxx::failure &e) {
81  if ((dynamic_cast<const pqxx::broken_connection *>(&e))) {
82  /* re-throw to outer loop to recover session. */
83  throw dut::broken(e.what());
84  }
85 
86  if (regex_match(e.what(), e_timeout))
87  throw dut::timeout(e.what());
88  else if (regex_match(e.what(), e_syntax))
89  throw dut::syntax(e.what());
90  else
91  throw dut::failure(e.what());
92  }
93 }
94 
95 
96 schema_pqxx::schema_pqxx(std::string &conninfo, bool no_catalog) : c(conninfo)
97 {
98  c.set_variable("application_name", "'" PACKAGE "::schema'");
99 
100  pqxx::work w(c);
101  pqxx::result r = w.exec("select version()");
102  version = r[0][0].as<string>();
103 
104  r = w.exec("SHOW server_version_num");
105  version_num = r[0][0].as<int>();
106 
107  // address the schema change in postgresql 11 that replaced proisagg and proiswindow with prokind
108  string procedure_is_aggregate = version_num < 110000 ? "proisagg" : "prokind = 'a'";
109  string procedure_is_window = version_num < 110000 ? "proiswindow" : "prokind = 'w'";
110 
111  cerr << "Loading types...";
112 
113  r = w.exec("select quote_ident(typname), oid, typdelim, typrelid, typelem, typarray, typtype "
114  "from pg_type ");
115 
116  for (auto row = r.begin(); row != r.end(); ++row) {
117  string name(row[0].as<string>());
118  OID oid(row[1].as<OID>());
119  string typdelim(row[2].as<string>());
120  OID typrelid(row[3].as<OID>());
121  OID typelem(row[4].as<OID>());
122  OID typarray(row[5].as<OID>());
123  string typtype(row[6].as<string>());
124  // if (schema == "pg_catalog")
125  // continue;
126  // if (schema == "information_schema")
127  // continue;
128 
129  pg_type *t = new pg_type(name,oid,typdelim[0],typrelid, typelem, typarray, typtype[0]);
130  oid2type[oid] = t;
131  name2type[name] = t;
132  types.push_back(t);
133  }
134 
135  booltype = name2type["bool"];
136  inttype = name2type["int4"];
137 
138  internaltype = name2type["internal"];
139  arraytype = name2type["anyarray"];
140 
141  cerr << "done." << endl;
142 
143  cerr << "Loading tables...";
144  r = w.exec("select table_name, "
145  "table_schema, "
146  "is_insertable_into, "
147  "table_type "
148  "from information_schema.tables");
149 
150  for (auto row = r.begin(); row != r.end(); ++row) {
151  string schema(row[1].as<string>());
152  string insertable(row[2].as<string>());
153  string table_type(row[3].as<string>());
154 
155  if (no_catalog && ((schema == "pg_catalog") || (schema == "information_schema")))
156  continue;
157 
158  tables.push_back(table(row[0].as<string>(),
159  schema,
160  ((insertable == "YES") ? true : false),
161  ((table_type == "BASE TABLE") ? true : false)));
162  }
163 
164  cerr << "done." << endl;
165 
166  cerr << "Loading columns and constraints...";
167 
168  for (auto t = tables.begin(); t != tables.end(); ++t) {
169  string q("select attname, "
170  "atttypid "
171  "from pg_attribute join pg_class c on( c.oid = attrelid ) "
172  "join pg_namespace n on n.oid = relnamespace "
173  "where not attisdropped "
174  "and attname not in "
175  "('xmin', 'xmax', 'ctid', 'cmin', 'cmax', 'tableoid', 'oid') ");
176  q += " and relname = " + w.quote(t->name);
177  q += " and nspname = " + w.quote(t->schema);
178 
179  r = w.exec(q);
180  for (auto row : r) {
181  column c(row[0].as<string>(), oid2type[row[1].as<OID>()]);
182  t->columns().push_back(c);
183  }
184 
185  q = "select conname from pg_class t "
186  "join pg_constraint c on (t.oid = c.conrelid) "
187  "where contype in ('f', 'u', 'p') ";
188  q += " and relnamespace = " " (select oid from pg_namespace where nspname = " + w.quote(t->schema) + ")";
189  q += " and relname = " + w.quote(t->name);
190 
191  for (auto row : w.exec(q)) {
192  t->constraints.push_back(row[0].as<string>());
193  }
194 
195  }
196  cerr << "done." << endl;
197 
198  cerr << "Loading operators...";
199 
200  r = w.exec("select oprname, oprleft,"
201  "oprright, oprresult "
202  "from pg_catalog.pg_operator "
203  "where 0 not in (oprresult, oprright, oprleft) ");
204  for (auto row : r) {
205  op o(row[0].as<string>(),
206  oid2type[row[1].as<OID>()],
207  oid2type[row[2].as<OID>()],
208  oid2type[row[3].as<OID>()]);
209  register_operator(o);
210  }
211 
212  cerr << "done." << endl;
213 
214  cerr << "Loading routines...";
215  r = w.exec("select (select nspname from pg_namespace where oid = pronamespace), oid, prorettype, proname "
216  "from pg_proc "
217  "where prorettype::regtype::text not in ('event_trigger', 'trigger', 'opaque', 'internal') "
218  "and proname <> 'pg_event_trigger_table_rewrite_reason' "
219  "and proname <> 'pg_event_trigger_table_rewrite_oid' "
220  "and proname !~ '^ri_fkey_' "
221  "and not (proretset or " + procedure_is_aggregate + " or " + procedure_is_window + ") ");
222 
223  for (auto row : r) {
224  routine proc(row[0].as<string>(),
225  row[1].as<string>(),
226  oid2type[row[2].as<long>()],
227  row[3].as<string>());
228  register_routine(proc);
229  }
230 
231  cerr << "done." << endl;
232 
233  cerr << "Loading routine parameters...";
234 
235  for (auto &proc : routines) {
236  string q("select unnest(proargtypes) "
237  "from pg_proc ");
238  q += " where oid = " + w.quote(proc.specific_name);
239 
240  r = w.exec(q);
241  for (auto row : r) {
242  sqltype *t = oid2type[row[0].as<OID>()];
243  assert(t);
244  proc.argtypes.push_back(t);
245  }
246  }
247  cerr << "done." << endl;
248 
249  cerr << "Loading aggregates...";
250  r = w.exec("select (select nspname from pg_namespace where oid = pronamespace), oid, prorettype, proname "
251  "from pg_proc "
252  "where prorettype::regtype::text not in ('event_trigger', 'trigger', 'opaque', 'internal') "
253  "and proname not in ('pg_event_trigger_table_rewrite_reason') "
254  "and proname not in ('percentile_cont', 'dense_rank', 'cume_dist', "
255  "'rank', 'test_rank', 'percent_rank', 'percentile_disc', 'mode', 'test_percentile_disc') "
256  "and proname !~ '^ri_fkey_' "
257  "and not (proretset or " + procedure_is_window + ") "
258  "and " + procedure_is_aggregate);
259 
260  for (auto row : r) {
261  routine proc(row[0].as<string>(),
262  row[1].as<string>(),
263  oid2type[row[2].as<OID>()],
264  row[3].as<string>());
265  register_aggregate(proc);
266  }
267 
268  cerr << "done." << endl;
269 
270  cerr << "Loading aggregate parameters...";
271 
272  for (auto &proc : aggregates) {
273  string q("select unnest(proargtypes) "
274  "from pg_proc ");
275  q += " where oid = " + w.quote(proc.specific_name);
276 
277  r = w.exec(q);
278  for (auto row : r) {
279  sqltype *t = oid2type[row[0].as<OID>()];
280  assert(t);
281  proc.argtypes.push_back(t);
282  }
283  }
284  cerr << "done." << endl;
285  c.disconnect();
286  generate_indexes();
287 }
288 
289 extern "C" {
290  void dut_libpq_notice_rx(void *arg, const PGresult *res);
291 }
292 
293 void dut_libpq_notice_rx(void *arg, const PGresult *res)
294 {
295  (void) arg;
296  (void) res;
297 }
298 
299 void dut_libpq::connect(std::string &conninfo)
300 {
301  if (conn) {
302  PQfinish(conn);
303  }
304  conn = PQconnectdb(conninfo.c_str());
305  if (PQstatus(conn) != CONNECTION_OK)
306  {
307  char *errmsg = PQerrorMessage(conn);
308  if (strlen(errmsg))
309  throw dut::broken(errmsg, "08001");
310  }
311 
312  command("set statement_timeout to '1s'");
313  command("set client_min_messages to 'ERROR';");
314  command("set application_name to '" PACKAGE "::dut';");
315 
316  PQsetNoticeReceiver(conn, dut_libpq_notice_rx, (void *) 0);
317 }
318 
319 dut_libpq::dut_libpq(std::string conninfo)
320  : conninfo_(conninfo)
321 {
322  connect(conninfo);
323 }
324 
325 void dut_libpq::command(const std::string &stmt)
326 {
327  if (!conn)
328  connect(conninfo_);
329  PGresult *res = PQexec(conn, stmt.c_str());
330 
331  switch (PQresultStatus(res)) {
332 
333  case PGRES_FATAL_ERROR:
334  default:
335  {
336  const char *errmsg = PQresultErrorMessage(res);
337  if (!errmsg || !strlen(errmsg))
338  errmsg = PQerrorMessage(conn);
339 
340  const char *sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
341  if (!sqlstate || !strlen(sqlstate))
342  sqlstate = (CONNECTION_OK != PQstatus(conn)) ? "08000" : "?????";
343 
344  std::string error_string(errmsg);
345  std::string sqlstate_string(sqlstate);
346  PQclear(res);
347 
348  if (CONNECTION_OK != PQstatus(conn)) {
349  PQfinish(conn);
350  conn = 0;
351  throw dut::broken(error_string.c_str(), sqlstate_string.c_str());
352  }
353  if (sqlstate_string == "42601")
354  throw dut::syntax(error_string.c_str(), sqlstate_string.c_str());
355  else
356  throw dut::failure(error_string.c_str(), sqlstate_string.c_str());
357  }
358 
359  case PGRES_NONFATAL_ERROR:
360  case PGRES_TUPLES_OK:
361  case PGRES_SINGLE_TUPLE:
362  case PGRES_COMMAND_OK:
363  PQclear(res);
364  return;
365  }
366 }
367 
368 void dut_libpq::test(const std::string &stmt)
369 {
370  command("ROLLBACK;");
371  command("BEGIN;");
372  command(stmt.c_str());
373  command("ROLLBACK;");
374 }
schema and dut classes for PostgreSQL
Definition: relmodel.hh:117
virtual bool consistent(struct sqltype *rvalue)
This function is used to model postgres-style pseudotypes.
Definition: postgres.cc:19
Definition: schema.hh:16