| 1 | #include "duckdb.hh" |
| 2 | #include "dbgen.hpp" |
| 3 | |
| 4 | #include <cassert> |
| 5 | #include <cstring> |
| 6 | #include <iostream> |
| 7 | #include <stdexcept> |
| 8 | #include <thread> |
| 9 | #include <chrono> |
| 10 | |
| 11 | #include <regex> |
| 12 | |
| 13 | using namespace duckdb; |
| 14 | using namespace std; |
| 15 | |
| 16 | static regex e_syntax("Query Error: syntax error at or near .*" ); |
| 17 | |
| 18 | duckdb_connection::duckdb_connection(string &conninfo) { |
| 19 | // in-memory database |
| 20 | database = make_unique<DuckDB>(nullptr); |
| 21 | connection = make_unique<Connection>(*database); |
| 22 | } |
| 23 | |
| 24 | void duckdb_connection::q(const char *query) { |
| 25 | auto result = connection->Query(query); |
| 26 | if (!result->success) { |
| 27 | throw runtime_error(result->error); |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | schema_duckdb::schema_duckdb(std::string &conninfo, bool no_catalog) : duckdb_connection(conninfo) { |
| 32 | // generate empty TPC-H schema |
| 33 | tpch::dbgen(0, *database); |
| 34 | |
| 35 | cerr << "Loading tables..." ; |
| 36 | auto result = connection->Query("SELECT * FROM sqlite_master() WHERE type IN ('table', 'view')" ); |
| 37 | if (!result->success) { |
| 38 | throw runtime_error(result->error); |
| 39 | } |
| 40 | for (size_t i = 0; i < result->collection.count; i++) { |
| 41 | auto type = result->collection.GetValue(0, i).str_value; |
| 42 | auto name = result->collection.GetValue(2, i).str_value; |
| 43 | bool view = type == "view" ; |
| 44 | table tab(name, "main" , !view, !view); |
| 45 | tables.push_back(tab); |
| 46 | } |
| 47 | cerr << "done." << endl; |
| 48 | |
| 49 | if (tables.size() == 0) { |
| 50 | throw std::runtime_error("No tables available in catalog!" ); |
| 51 | } |
| 52 | |
| 53 | cerr << "Loading columns and constraints..." ; |
| 54 | |
| 55 | for (auto t = tables.begin(); t != tables.end(); ++t) { |
| 56 | result = connection->Query("PRAGMA table_info('" + t->name + "')" ); |
| 57 | if (!result->success) { |
| 58 | throw runtime_error(result->error); |
| 59 | } |
| 60 | for (size_t i = 0; i < result->collection.count; i++) { |
| 61 | auto name = result->collection.GetValue(1, i).str_value; |
| 62 | auto type = result->collection.GetValue(2, i).str_value; |
| 63 | column c(name, sqltype::get(type)); |
| 64 | t->columns().push_back(c); |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | cerr << "done." << endl; |
| 69 | |
| 70 | #define BINOP(n, t) \ |
| 71 | do { \ |
| 72 | op o(#n, sqltype::get(#t), sqltype::get(#t), sqltype::get(#t)); \ |
| 73 | register_operator(o); \ |
| 74 | } while (0) |
| 75 | |
| 76 | // BINOP(||, TEXT); |
| 77 | BINOP(*, INTEGER); |
| 78 | BINOP(/, INTEGER); |
| 79 | |
| 80 | BINOP(+, INTEGER); |
| 81 | BINOP(-, INTEGER); |
| 82 | |
| 83 | // BINOP(>>, INTEGER); |
| 84 | // BINOP(<<, INTEGER); |
| 85 | |
| 86 | // BINOP(&, INTEGER); |
| 87 | // BINOP(|, INTEGER); |
| 88 | |
| 89 | BINOP(<, INTEGER); |
| 90 | BINOP(<=, INTEGER); |
| 91 | BINOP(>, INTEGER); |
| 92 | BINOP(>=, INTEGER); |
| 93 | |
| 94 | BINOP(=, INTEGER); |
| 95 | BINOP(<>, INTEGER); |
| 96 | BINOP(IS, INTEGER); |
| 97 | BINOP(IS NOT, INTEGER); |
| 98 | |
| 99 | BINOP(AND, INTEGER); |
| 100 | BINOP(OR, INTEGER); |
| 101 | |
| 102 | #define FUNC(n, r) \ |
| 103 | do { \ |
| 104 | routine proc("", "", sqltype::get(#r), #n); \ |
| 105 | register_routine(proc); \ |
| 106 | } while (0) |
| 107 | |
| 108 | #define FUNC1(n, r, a) \ |
| 109 | do { \ |
| 110 | routine proc("", "", sqltype::get(#r), #n); \ |
| 111 | proc.argtypes.push_back(sqltype::get(#a)); \ |
| 112 | register_routine(proc); \ |
| 113 | } while (0) |
| 114 | |
| 115 | #define FUNC2(n, r, a, b) \ |
| 116 | do { \ |
| 117 | routine proc("", "", sqltype::get(#r), #n); \ |
| 118 | proc.argtypes.push_back(sqltype::get(#a)); \ |
| 119 | proc.argtypes.push_back(sqltype::get(#b)); \ |
| 120 | register_routine(proc); \ |
| 121 | } while (0) |
| 122 | |
| 123 | #define FUNC3(n, r, a, b, c) \ |
| 124 | do { \ |
| 125 | routine proc("", "", sqltype::get(#r), #n); \ |
| 126 | proc.argtypes.push_back(sqltype::get(#a)); \ |
| 127 | proc.argtypes.push_back(sqltype::get(#b)); \ |
| 128 | proc.argtypes.push_back(sqltype::get(#c)); \ |
| 129 | register_routine(proc); \ |
| 130 | } while (0) |
| 131 | |
| 132 | // FUNC(last_insert_rowid, INTEGER); |
| 133 | // FUNC(random, INTEGER); |
| 134 | // FUNC(sqlite_source_id, TEXT); |
| 135 | // FUNC(sqlite_version, TEXT); |
| 136 | // FUNC(total_changes, INTEGER); |
| 137 | |
| 138 | FUNC1(abs, INTEGER, REAL); |
| 139 | // FUNC1(hex, TEXT, TEXT); |
| 140 | // FUNC1(length, INTEGER, TEXT); |
| 141 | // FUNC1(lower, TEXT, TEXT); |
| 142 | // FUNC1(ltrim, TEXT, TEXT); |
| 143 | // FUNC1(quote, TEXT, TEXT); |
| 144 | // FUNC1(randomblob, TEXT, INTEGER); |
| 145 | // FUNC1(round, INTEGER, REAL); |
| 146 | // FUNC1(rtrim, TEXT, TEXT); |
| 147 | // FUNC1(soundex, TEXT, TEXT); |
| 148 | // FUNC1(sqlite_compileoption_get, TEXT, INTEGER); |
| 149 | // FUNC1(sqlite_compileoption_used, INTEGER, TEXT); |
| 150 | // FUNC1(trim, TEXT, TEXT); |
| 151 | // FUNC1(typeof, TEXT, INTEGER); |
| 152 | // FUNC1(typeof, TEXT, NUMERIC); |
| 153 | // FUNC1(typeof, TEXT, REAL); |
| 154 | // FUNC1(typeof, TEXT, TEXT); |
| 155 | // FUNC1(unicode, INTEGER, TEXT); |
| 156 | // FUNC1(upper, TEXT, TEXT); |
| 157 | // FUNC1(zeroblob, TEXT, INTEGER); |
| 158 | |
| 159 | // FUNC2(glob, INTEGER, TEXT, TEXT); |
| 160 | // FUNC2(instr, INTEGER, TEXT, TEXT); |
| 161 | // FUNC2(like, INTEGER, TEXT, TEXT); |
| 162 | // FUNC2(ltrim, TEXT, TEXT, TEXT); |
| 163 | // FUNC2(rtrim, TEXT, TEXT, TEXT); |
| 164 | // FUNC2(trim, TEXT, TEXT, TEXT); |
| 165 | // FUNC2(round, INTEGER, REAL, INTEGER); |
| 166 | // FUNC2(substr, TEXT, TEXT, INTEGER); |
| 167 | |
| 168 | // FUNC3(substr, TEXT, TEXT, INTEGER, INTEGER); |
| 169 | // FUNC3(replace, TEXT, TEXT, TEXT, TEXT); |
| 170 | |
| 171 | #define AGG(n, r, a) \ |
| 172 | do { \ |
| 173 | routine proc("", "", sqltype::get(#r), #n); \ |
| 174 | proc.argtypes.push_back(sqltype::get(#a)); \ |
| 175 | register_aggregate(proc); \ |
| 176 | } while (0) |
| 177 | |
| 178 | AGG(avg, INTEGER, INTEGER); |
| 179 | AGG(avg, REAL, REAL); |
| 180 | AGG(count, INTEGER, REAL); |
| 181 | AGG(count, INTEGER, TEXT); |
| 182 | AGG(count, INTEGER, INTEGER); |
| 183 | // AGG(group_concat, TEXT, TEXT); |
| 184 | AGG(max, REAL, REAL); |
| 185 | AGG(max, INTEGER, INTEGER); |
| 186 | AGG(min, REAL, REAL); |
| 187 | AGG(min, INTEGER, INTEGER); |
| 188 | AGG(sum, REAL, REAL); |
| 189 | AGG(sum, INTEGER, INTEGER); |
| 190 | // AGG(total, REAL, INTEGER); |
| 191 | // AGG(total, REAL, REAL); |
| 192 | |
| 193 | booltype = sqltype::get("INTEGER" ); |
| 194 | inttype = sqltype::get("INTEGER" ); |
| 195 | |
| 196 | internaltype = sqltype::get("internal" ); |
| 197 | arraytype = sqltype::get("ARRAY" ); |
| 198 | |
| 199 | true_literal = "1" ; |
| 200 | false_literal = "0" ; |
| 201 | |
| 202 | generate_indexes(); |
| 203 | } |
| 204 | |
| 205 | dut_duckdb::dut_duckdb(std::string &conninfo) : duckdb_connection(conninfo) { |
| 206 | cerr << "Generating TPC-H..." ; |
| 207 | tpch::dbgen(0.1, *database); |
| 208 | cerr << "done." << endl; |
| 209 | // q("PRAGMA main.auto_vacuum = 2"); |
| 210 | } |
| 211 | |
| 212 | volatile bool is_active = false; |
| 213 | // timeout is 10ms * TIMEOUT_TICKS |
| 214 | #define TIMEOUT_TICKS 50 |
| 215 | |
| 216 | void sleep_thread(Connection *connection) { |
| 217 | for (size_t i = 0; i < TIMEOUT_TICKS && is_active; i++) { |
| 218 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
| 219 | } |
| 220 | if (is_active) { |
| 221 | connection->Interrupt(); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | void dut_duckdb::test(const std::string &stmt) { |
| 226 | is_active = true; |
| 227 | thread interrupt_thread(sleep_thread, connection.get()); |
| 228 | auto result = connection->Query(stmt); |
| 229 | is_active = false; |
| 230 | interrupt_thread.join(); |
| 231 | |
| 232 | if (!result->success) { |
| 233 | auto error = result->error.c_str(); |
| 234 | try { |
| 235 | if (regex_match(error, e_syntax)) |
| 236 | throw dut::syntax(error); |
| 237 | else |
| 238 | throw dut::failure(error); |
| 239 | } catch (dut::failure &e) { |
| 240 | throw; |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | |