diff --git a/grammar.cc b/grammar.cc index 62aa8e9..775408e 100644 --- a/grammar.cc +++ b/grammar.cc @@ -20,10 +20,12 @@ shared_ptr table_ref::factory(prod *p) { if (d6() > 3) return make_shared(p); } - if (d6() > 3) - return make_shared(p); - else - return make_shared(p); + // disable table sample since both TiDB and CRDB don't support it + return make_shared(p); + // if (d6() > 3) + // return make_shared(p); + // else + // return make_shared(p); } catch (runtime_error &e) { p->retry(); } @@ -319,7 +321,7 @@ query_spec::query_spec(prod *p, struct scope *s, bool lateral) : if (lateral) scope->refs = s->refs; - + from_clause = make_shared(this); select_list = make_shared(this); @@ -327,6 +329,7 @@ query_spec::query_spec(prod *p, struct scope *s, bool lateral) : search = bool_expr::factory(this); + // for comparing MySQL and TiDB, maybe disable limit clause here if (d6() > 2) { ostringstream cons; cons << "limit " << d100() + d100(); @@ -467,20 +470,20 @@ shared_ptr statement_factory(struct scope *s) { try { s->new_stmt(); - if (d42() == 1) - return make_shared((struct prod *)0, s); - if (d42() == 1) - return make_shared((struct prod *)0, s); - else if (d42() == 1) - return make_shared((struct prod *)0, s); - else if (d42() == 1) { - return make_shared((struct prod *)0, s); - } else if (d42() == 1) - return make_shared((struct prod *)0, s); - else if (d6() > 4) - return make_shared((struct prod *)0, s); - else if (d6() > 5) - return make_shared((struct prod *)0, s); + // if (d42() == 1) + // return make_shared((struct prod *)0, s); + // if (d42() == 1) + // return make_shared((struct prod *)0, s); + // else if (d42() == 1) + // return make_shared((struct prod *)0, s); + // else if (d42() == 1) + // return make_shared((struct prod *)0, s); + // else if (d42() == 1) + // return make_shared((struct prod *)0, s); + // else if (d6() > 4) + // return make_shared((struct prod *)0, s); + // else if (d6() > 5) + // return make_shared((struct prod *)0, s); return make_shared((struct prod *)0, s); } catch (runtime_error &e) { return statement_factory(s); @@ -510,18 +513,17 @@ common_table_expression::common_table_expression(prod *parent, struct scope *s) } while (d6() > 2); - retry: - do { - auto pick = random_pick(s->tables); - scope->tables.push_back(pick); - } while (d6() > 3); - try { - query = make_shared(this, scope); - } catch (runtime_error &e) { - retry(); - goto retry; - } - + retry: + do { + auto pick = random_pick(s->tables); + scope->tables.push_back(pick); + } while (d6() > 3); + try { + query = make_shared(this, scope); + } catch (runtime_error &e) { + retry(); + goto retry; + } } void common_table_expression::out(std::ostream &out) diff --git a/mysql.cc b/mysql.cc index b45622f..fb50644 100644 --- a/mysql.cc +++ b/mysql.cc @@ -1,5 +1,8 @@ #include "mysql.hh" #include +#include +#include +#include using namespace std; @@ -33,6 +36,9 @@ void mysql_connection::parse_connection_string(std::string &conninfo) { } mysql_connection::mysql_connection(std::string &conninfo) { + if (conninfo == "") { + return; + } mysql_init(&mysql); parse_connection_string(conninfo); @@ -87,7 +93,8 @@ std::string parse_column_type(const char* column_type) { !strcmp(column_type,"BLOB") || !strcmp(column_type,"TINYBLOB") || !strcmp(column_type,"MEDIUMBLOB") || - !strcmp(column_type,"LONGBLOB")) { + !strcmp(column_type,"LONGBLOB") || + !strcmp(column_type,"VARBINARY")) { return std::string("BINARY"); } @@ -107,6 +114,9 @@ std::string parse_column_type(const char* column_type) { schema_mysql::schema_mysql(std::string &conninfo, bool no_catalog) : mysql_connection(conninfo) { + if (conninfo == "") { + return; + } (void)no_catalog; MYSQL_RES *result; MYSQL_ROW row; @@ -119,6 +129,7 @@ schema_mysql::schema_mysql(std::string &conninfo, bool no_catalog) throw std::runtime_error(mysql_error(&mysql)); } result = mysql_store_result(&mysql); + while ((row = mysql_fetch_row(result))) { string table_name; string schema_name; @@ -135,6 +146,8 @@ schema_mysql::schema_mysql(std::string &conninfo, bool no_catalog) continue; } + cout << row[0] << row[1] << insertable << base_table << endl; + table tab(row[0], row[1], insertable, base_table); tables.push_back(tab); } @@ -267,8 +280,13 @@ dut_mysql::dut_mysql(std::string& conninfo) {} void dut_mysql::test(const std::string &stmt) { + // remove tablesample syntax which mysql don't support + std::string r_stmt = std::regex_replace(stmt, std::regex("tablesample.+?\n"), " \n"); + r_stmt = std::regex_replace(r_stmt, std::regex("\n"), " "); + r_stmt = std::regex_replace(r_stmt, std::regex("\\s+"), " "); + // cout << r_stmt << endl; MYSQL_RES *result; - int rc = mysql_real_query(&mysql, stmt.c_str(), stmt.length()); + int rc = mysql_real_query(&mysql, r_stmt.c_str(), r_stmt.length()); if(rc){ unsigned int err_num = mysql_errno(&mysql); const char* err = mysql_error(&mysql); @@ -286,6 +304,11 @@ void dut_mysql::test(const std::string &stmt) { else { throw std::runtime_error(err); } + } else { + ofstream sqlfile; + sqlfile.open("./sql/success-sql.txt", std::ios_base::app); + sqlfile << r_stmt << endl; + sqlfile.close(); } result = mysql_store_result(&mysql); mysql_free_result(result); diff --git a/sqlsmith.cc b/sqlsmith.cc index d0432f6..9f0b299 100644 --- a/sqlsmith.cc +++ b/sqlsmith.cc @@ -59,12 +59,18 @@ extern "C" void cerr_log_handler(int) exit(1); } +bool starts_with(const std::string& s, const std::string& prefix) { + auto size = prefix.size(); + if (s.size() < size) return false; + return std::equal(std::begin(prefix), std::end(prefix), std::begin(s)); +} + int main(int argc, char *argv[]) { cerr << PACKAGE_NAME " " GITREV << endl; map options; - regex optregex("--(help|log-to|verbose|target|sqlite|monetdb|mysql|version|dump-all-graphs|dump-all-queries|seed|dry-run|max-queries|rng-state|exclude-catalog)(?:=((?:.|\n)*))?"); + regex optregex("--(help|log-to|verbose|target|sqlite|monetdb|mysql|version|dump-all-graphs|dump-all-queries|seed|dry-run|max-queries|rng-state|exclude-catalog|abtest)(?:=((?:.|\n)*))?"); for(char **opt = argv+1 ;opt < argv+argc; opt++) { smatch match; @@ -86,7 +92,7 @@ int main(int argc, char *argv[]) #ifdef HAVE_MONETDB " --monetdb=connstr MonetDB database to send queries to" < schema; + string target, abtest_target; + shared_ptr sch; if (options.count("sqlite")) { + target = "sqlite"; #ifdef HAVE_LIBSQLITE3 - schema = make_shared(options["sqlite"], options.count("exclude-catalog")); + sch = make_shared(options["sqlite"], options.count("exclude-catalog")); #else - cerr << "Sorry, " PACKAGE_NAME " was compiled without SQLite support." << endl; - return 1; + cerr << "Sorry, " PACKAGE_NAME " was compiled without SQLite support." << endl; + return 1; #endif } else if(options.count("monetdb")) { + target = "monetdb"; #ifdef HAVE_MONETDB - schema = make_shared(options["monetdb"]); + sch = make_shared(options["monetdb"]); #else - cerr << "Sorry, " PACKAGE_NAME " was compiled without MonetDB support." << endl; - return 1; + cerr << "Sorry, " PACKAGE_NAME " was compiled without MonetDB support." << endl; + return 1; #endif - } - else if(options.count("mysql")) { - schema = make_shared(options["mysql"], options.count("exclude-catalog")); } - else - schema = make_shared(options["target"], options.count("exclude-catalog")); + else if(options.count("mysql")) { + target = "mysql"; + sch = make_shared(options["mysql"], options.count("exclude-catalog")); + } else { + sch = make_shared(options["target"], options.count("exclude-catalog")); + } scope scope; long queries_generated = 0; - schema->fill_scope(scope); + sch->fill_scope(scope); + + // handle abtest options + // if (options.count("abtest")) { + // if (options["abtest"] == "pgsql") { + // string conn = "host=172.16.4.253 port=5432 dbname=sakila user=postgres"; + // abtest_target = "pgsql"; + // shared_ptr abtest_sch; + // abtest_sch = make_shared(conn, false); + // abtest_sch->fill_scope(abtest_scope); + // } else if (options["abtest"] == "mysql") { + // string conn = ""; + // abtest_target = "mysql"; + // shared_ptr abtest_sch; + // abtest_sch = make_shared(conn, false); + // abtest_sch->fill_scope(abtest_scope); + // } + // } if (options.count("rng-state")) { - istringstream(options["rng-state"]) >> smith::rng; + istringstream(options["rng-state"]) >> smith::rng; } else { - smith::rng.seed(options.count("seed") ? stoi(options["seed"]) : getpid()); + smith::rng.seed(options.count("seed") ? stoi(options["seed"]) : getpid()); } vector > loggers; @@ -143,112 +171,123 @@ int main(int argc, char *argv[]) loggers.push_back(make_shared()); if (options.count("log-to")) - loggers.push_back(make_shared( - options.count("sqlite") ? options["sqlite"] : options["target"], - options["log-to"], *schema)); + loggers.push_back(make_shared( + options.count("sqlite") ? options["sqlite"] : options["target"], + options["log-to"], *sch)); if (options.count("verbose")) { - auto l = make_shared(); - global_cerr_logger = &*l; - loggers.push_back(l); - signal(SIGINT, cerr_log_handler); + auto l = make_shared(); + global_cerr_logger = &*l; + loggers.push_back(l); + signal(SIGINT, cerr_log_handler); } if (options.count("dump-all-graphs")) - loggers.push_back(make_shared()); + loggers.push_back(make_shared()); if (options.count("dump-all-queries")) - loggers.push_back(make_shared()); + loggers.push_back(make_shared()); if (options.count("dry-run")) { - while (1) { - shared_ptr gen = statement_factory(&scope); - gen->out(cout); - for (auto l : loggers) - l->generated(*gen); - cout << ";" << endl; - queries_generated++; - - if (options.count("max-queries") - && (queries_generated >= stol(options["max-queries"]))) - return 0; - } + while (1) { + cout << "generate main" << endl; + shared_ptr gen = statement_factory(&scope); + cout << target << endl; + gen->out(cout); + // if (options.count("abtest")) { + // cout << "generate sub" << endl; + // // schema *tmp_schema = scope.schema; + // // scope.schema = scope.abtest_schema; + // cout << endl << abtest_target << endl; + // gen->out(cout); + // // scope.schema = tmp_schema; + // exit(0); + // } + for (auto l : loggers) + l->generated(*gen); + cout << ";" << endl; + queries_generated++; + + if (options.count("max-queries") + && (queries_generated >= stol(options["max-queries"]))) + return 0; + } } shared_ptr dut; if (options.count("sqlite")) { #ifdef HAVE_LIBSQLITE3 - dut = make_shared(options["sqlite"]); + dut = make_shared(options["sqlite"]); #else - cerr << "Sorry, " PACKAGE_NAME " was compiled without SQLite support." << endl; - return 1; + cerr << "Sorry, " PACKAGE_NAME " was compiled without SQLite support." << endl; + return 1; #endif } else if(options.count("monetdb")) { -#ifdef HAVE_MONETDB - dut = make_shared(options["monetdb"]); +#ifdef HAVE_MONETDB + dut = make_shared(options["monetdb"]); #else - cerr << "Sorry, " PACKAGE_NAME " was compiled without MonetDB support." << endl; - return 1; + cerr << "Sorry, " PACKAGE_NAME " was compiled without MonetDB support." << endl; + return 1; #endif - } + } else if(options.count("mysql")) { - dut = make_shared(options["mysql"]); + dut = make_shared(options["mysql"]); } else - dut = make_shared(options["target"]); + dut = make_shared(options["target"]); while (1) /* Loop to recover connection loss */ { - try { - while (1) { /* Main loop */ - - if (options.count("max-queries") - && (++queries_generated > stol(options["max-queries"]))) { - if (global_cerr_logger) - global_cerr_logger->report(); - return 0; - } - - /* Invoke top-level production to generate AST */ - shared_ptr gen = statement_factory(&scope); - - for (auto l : loggers) - l->generated(*gen); - - /* Generate SQL from AST */ - ostringstream s; - gen->out(s); - - /* Try to execute it */ - try { - dut->test(s.str()); - for (auto l : loggers) - l->executed(*gen); - } catch (const dut::failure &e) { - for (auto l : loggers) - try { - l->error(*gen, e); - } catch (runtime_error &e) { - cerr << endl << "log failed: " << typeid(*l).name() << ": " - << e.what() << endl; - } - if ((dynamic_cast(&e))) { - /* re-throw to outer loop to recover session. */ - throw; - } - } - } - } - catch (const dut::broken &e) { - /* Give server some time to recover. */ - this_thread::sleep_for(milliseconds(1000)); - } + try { + while (1) { /* Main loop */ + + if (options.count("max-queries") + && (++queries_generated > stol(options["max-queries"]))) { + if (global_cerr_logger) + global_cerr_logger->report(); + return 0; + } + + /* Invoke top-level production to generate AST */ + shared_ptr gen = statement_factory(&scope); + + for (auto l : loggers) + l->generated(*gen); + + /* Generate SQL from AST */ + ostringstream s; + gen->out(s); + + /* Try to execute it */ + try { + dut->test(s.str()); + for (auto l : loggers) + l->executed(*gen); + } catch (const dut::failure &e) { + for (auto l : loggers) + try { + l->error(*gen, e); + } catch (runtime_error &e) { + cerr << endl << "log failed: " << typeid(*l).name() << ": " + << e.what() << endl; + } + if ((dynamic_cast(&e))) { + /* re-throw to outer loop to recover session. */ + throw; + } + } + } + } + catch (const dut::broken &e) { + /* Give server some time to recover. */ + this_thread::sleep_for(milliseconds(1000)); + } } } - catch (const exception &e) { - cerr << e.what() << endl; - return 1; + catch (const exception &e) { + cerr << e.what() << endl; + return 1; } }