| 1 | #include "catch.hpp" | 
|---|
| 2 | #include "duckdb/common/file_system.hpp" | 
|---|
| 3 | #include "duckdb/common/types/date.hpp" | 
|---|
| 4 | #include "test_helpers.hpp" | 
|---|
| 5 |  | 
|---|
| 6 | using namespace duckdb; | 
|---|
| 7 | using namespace std; | 
|---|
| 8 |  | 
|---|
| 9 | TEST_CASE( "Basic prepared statements", "[prepared]") { | 
|---|
| 10 | unique_ptr<QueryResult> result; | 
|---|
| 11 | DuckDB db(nullptr); | 
|---|
| 12 | Connection con(db); | 
|---|
| 13 |  | 
|---|
| 14 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS SELECT CAST($1 AS INTEGER), CAST($2 AS STRING)")); | 
|---|
| 15 | result = con.Query( "EXECUTE s1(42, 'dpfkg')"); | 
|---|
| 16 |  | 
|---|
| 17 | REQUIRE(CHECK_COLUMN(result, 0, {42})); | 
|---|
| 18 | REQUIRE(CHECK_COLUMN(result, 1, { "dpfkg"})); | 
|---|
| 19 |  | 
|---|
| 20 | result = con.Query( "EXECUTE s1(43, 'asdf')"); | 
|---|
| 21 | REQUIRE(CHECK_COLUMN(result, 0, {43})); | 
|---|
| 22 | REQUIRE(CHECK_COLUMN(result, 1, { "asdf"})); | 
|---|
| 23 |  | 
|---|
| 24 | // not enough params | 
|---|
| 25 | REQUIRE_FAIL(con.Query( "EXECUTE s1(43)")); | 
|---|
| 26 | // too many | 
|---|
| 27 | REQUIRE_FAIL(con.Query( "EXECUTE s1(43, 'asdf', 42)")); | 
|---|
| 28 | // wrong non-castable types | 
|---|
| 29 | REQUIRE_FAIL(con.Query( "EXECUTE s1('asdf', 'asdf')")); | 
|---|
| 30 |  | 
|---|
| 31 | REQUIRE_NO_FAIL(con.Query( "DEALLOCATE s1")); | 
|---|
| 32 |  | 
|---|
| 33 | // we can deallocate non-existing statements | 
|---|
| 34 | REQUIRE_NO_FAIL(con.Query( "DEALLOCATE s2")); | 
|---|
| 35 |  | 
|---|
| 36 | // now its gone | 
|---|
| 37 | REQUIRE_FAIL(con.Query( "EXECUTE s1(42, 'dpfkg')")); | 
|---|
| 38 |  | 
|---|
| 39 | // prepare a statement that cannot be prepared | 
|---|
| 40 | REQUIRE_FAIL(con.Query( "PREPARE EXPLAIN SELECT 42")); | 
|---|
| 41 |  | 
|---|
| 42 | REQUIRE_FAIL(con.Query( "PREPARE CREATE TABLE a(i INTEGER)")); | 
|---|
| 43 | REQUIRE_FAIL(con.Query( "SELECT * FROM a;")); | 
|---|
| 44 |  | 
|---|
| 45 | // type will be resolved to "double" | 
|---|
| 46 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS SELECT $1+$2")); | 
|---|
| 47 | REQUIRE_NO_FAIL(con.Query( "DEALLOCATE s1")); | 
|---|
| 48 |  | 
|---|
| 49 | // but this works | 
|---|
| 50 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS SELECT NOT($1), 10+$2, $3+20, 4 IN (2, 3, $4), $5 IN (2, 3, 4)")); | 
|---|
| 51 |  | 
|---|
| 52 | result = con.Query( "EXECUTE s1(1, 2, 3, 4, 2)"); | 
|---|
| 53 | REQUIRE(CHECK_COLUMN(result, 0, {false})); | 
|---|
| 54 | REQUIRE(CHECK_COLUMN(result, 1, {12})); | 
|---|
| 55 | REQUIRE(CHECK_COLUMN(result, 2, {23})); | 
|---|
| 56 | REQUIRE(CHECK_COLUMN(result, 3, {true})); | 
|---|
| 57 | REQUIRE(CHECK_COLUMN(result, 4, {true})); | 
|---|
| 58 |  | 
|---|
| 59 | // cannot resolve these types | 
|---|
| 60 | REQUIRE_FAIL(con.Query( "PREPARE s1 AS SELECT $1")); | 
|---|
| 61 | REQUIRE_FAIL(con.Query( "PREPARE s1 AS SELECT (SELECT $1)")); | 
|---|
| 62 | REQUIRE_FAIL(con.Query( "PREPARE s1 AS SELECT $1=$2")); | 
|---|
| 63 | } | 
|---|
| 64 |  | 
|---|
| 65 | TEST_CASE( "Prepared statements and subqueries", "[prepared]") { | 
|---|
| 66 | unique_ptr<QueryResult> result; | 
|---|
| 67 | DuckDB db(nullptr); | 
|---|
| 68 | Connection con(db); | 
|---|
| 69 |  | 
|---|
| 70 | // simple subquery | 
|---|
| 71 | REQUIRE_NO_FAIL(con.Query( "PREPARE v1 AS SELECT * FROM (SELECT $1::INTEGER) sq1;")); | 
|---|
| 72 |  | 
|---|
| 73 | result = con.Query( "EXECUTE v1(42)"); | 
|---|
| 74 | REQUIRE(CHECK_COLUMN(result, 0, {42})); | 
|---|
| 75 |  | 
|---|
| 76 | // subquery with non-fulfillable predicate | 
|---|
| 77 | REQUIRE_NO_FAIL(con.Query( "PREPARE v2 AS SELECT * FROM (SELECT $1::INTEGER WHERE 1=0) sq1;")); | 
|---|
| 78 |  | 
|---|
| 79 | result = con.Query( "EXECUTE v2(42)"); | 
|---|
| 80 | REQUIRE(CHECK_COLUMN(result, 0, {})); | 
|---|
| 81 |  | 
|---|
| 82 | // prepared statement in correlated scalar subquery | 
|---|
| 83 | REQUIRE_NO_FAIL(con.Query( "PREPARE v3 AS SELECT (SELECT $1::INT+sq1.i) FROM (SELECT 42 AS i) sq1;")); | 
|---|
| 84 |  | 
|---|
| 85 | result = con.Query( "EXECUTE v3(42)"); | 
|---|
| 86 | REQUIRE(CHECK_COLUMN(result, 0, {84})); | 
|---|
| 87 |  | 
|---|
| 88 | // prepared statement in nested correlated scalar subquery | 
|---|
| 89 | REQUIRE_NO_FAIL( | 
|---|
| 90 | con.Query( "PREPARE v4 AS SELECT (SELECT (SELECT $1::INT+sq1.i)+$2::INT+sq1.i) FROM (SELECT 42 AS i) sq1;")); | 
|---|
| 91 |  | 
|---|
| 92 | result = con.Query( "EXECUTE v4(20, 20)"); | 
|---|
| 93 | REQUIRE(CHECK_COLUMN(result, 0, {124})); | 
|---|
| 94 | } | 
|---|
| 95 |  | 
|---|
| 96 | TEST_CASE( "PREPARE for SELECT clause", "[prepared]") { | 
|---|
| 97 | unique_ptr<QueryResult> result; | 
|---|
| 98 | DuckDB db(nullptr); | 
|---|
| 99 | Connection con(db); | 
|---|
| 100 |  | 
|---|
| 101 | REQUIRE_NO_FAIL(con.Query( "CREATE TABLE a (i TINYINT)")); | 
|---|
| 102 | REQUIRE_NO_FAIL(con.Query( "INSERT INTO a VALUES (42)")); | 
|---|
| 103 | REQUIRE_NO_FAIL(con.Query( "PREPARE s3 AS SELECT * FROM a WHERE i=$1")); | 
|---|
| 104 |  | 
|---|
| 105 | REQUIRE_FAIL(con.Query( "EXECUTE s3(10000)")); | 
|---|
| 106 |  | 
|---|
| 107 | result = con.Query( "EXECUTE s3(42)"); | 
|---|
| 108 | REQUIRE(result->success); | 
|---|
| 109 | REQUIRE(CHECK_COLUMN(result, 0, {42})); | 
|---|
| 110 |  | 
|---|
| 111 | result = con.Query( "EXECUTE s3(84)"); | 
|---|
| 112 | REQUIRE(result->success); | 
|---|
| 113 | REQUIRE(CHECK_COLUMN(result, 0, {})); | 
|---|
| 114 |  | 
|---|
| 115 | REQUIRE_NO_FAIL(con.Query( "DEALLOCATE s3")); | 
|---|
| 116 |  | 
|---|
| 117 | // can't run a query with a param without PREPARE | 
|---|
| 118 | REQUIRE_FAIL(con.Query( "SELECT * FROM a WHERE i=$1")); | 
|---|
| 119 | // also can't run a query with a param when casting | 
|---|
| 120 | REQUIRE_FAIL(con.Query( "SELECT * FROM a WHERE i=CAST($1 AS VARCHAR)")); | 
|---|
| 121 | } | 
|---|
| 122 |  | 
|---|
| 123 | TEST_CASE( "PREPARE for INSERT", "[prepared]") { | 
|---|
| 124 | unique_ptr<QueryResult> result; | 
|---|
| 125 | DuckDB db(nullptr); | 
|---|
| 126 | Connection con(db); | 
|---|
| 127 |  | 
|---|
| 128 | REQUIRE_NO_FAIL(con.Query( "CREATE TABLE b (i TINYINT)")); | 
|---|
| 129 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS INSERT INTO b VALUES (cast($1 as tinyint)), ($2 + 1), ($3)")); | 
|---|
| 130 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s1 (42, 41, 42)")); | 
|---|
| 131 |  | 
|---|
| 132 | result = con.Query( "SELECT * FROM b"); | 
|---|
| 133 | REQUIRE(CHECK_COLUMN(result, 0, {42, 42, 42})); | 
|---|
| 134 | REQUIRE_FAIL(con.Query( "EXECUTE s1 (42, 41, 10000)")); | 
|---|
| 135 |  | 
|---|
| 136 | REQUIRE_NO_FAIL(con.Query( "CREATE TABLE c (i INTEGER)")); | 
|---|
| 137 | REQUIRE_NO_FAIL(con.Query( "PREPARE s2 AS INSERT INTO c VALUES ($1)")); | 
|---|
| 138 |  | 
|---|
| 139 | for (size_t i = 0; i < 1000; i++) { | 
|---|
| 140 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s2("+ to_string(i) + ")")); | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|
| 143 | result = con.Query( "SELECT COUNT(*), MIN(i), MAX(i) FROM c"); | 
|---|
| 144 | REQUIRE(CHECK_COLUMN(result, 0, {1000})); | 
|---|
| 145 | REQUIRE(CHECK_COLUMN(result, 1, {0})); | 
|---|
| 146 | REQUIRE(CHECK_COLUMN(result, 2, {999})); | 
|---|
| 147 |  | 
|---|
| 148 | // can't drop table because we still have a prepared statement on it | 
|---|
| 149 | REQUIRE_FAIL(con.Query( "DROP TABLE b")); | 
|---|
| 150 | REQUIRE_FAIL(con.Query( "DROP TABLE c")); | 
|---|
| 151 |  | 
|---|
| 152 | REQUIRE_NO_FAIL(con.Query( "DEALLOCATE s2")); | 
|---|
| 153 | REQUIRE_NO_FAIL(con.Query( "DEALLOCATE s1")); | 
|---|
| 154 |  | 
|---|
| 155 | // now we can | 
|---|
| 156 | REQUIRE_NO_FAIL(con.Query( "DROP TABLE b")); | 
|---|
| 157 | REQUIRE_NO_FAIL(con.Query( "DROP TABLE c")); | 
|---|
| 158 | } | 
|---|
| 159 |  | 
|---|
| 160 | TEST_CASE( "PREPARE for INSERT with dates", "[prepared]") { | 
|---|
| 161 | unique_ptr<QueryResult> result; | 
|---|
| 162 | DuckDB db(nullptr); | 
|---|
| 163 | Connection con(db); | 
|---|
| 164 |  | 
|---|
| 165 | // prepared DATE insert | 
|---|
| 166 | REQUIRE_NO_FAIL(con.Query( "CREATE TABLE dates(d DATE)")); | 
|---|
| 167 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS INSERT INTO dates VALUES ($1)")); | 
|---|
| 168 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s1 (DATE '1992-01-01')")); | 
|---|
| 169 |  | 
|---|
| 170 | result = con.Query( "SELECT * FROM dates"); | 
|---|
| 171 | REQUIRE(CHECK_COLUMN(result, 0, {Value::DATE(1992, 1, 1)})); | 
|---|
| 172 |  | 
|---|
| 173 | REQUIRE_NO_FAIL(con.Query( "DELETE FROM dates")); | 
|---|
| 174 |  | 
|---|
| 175 | auto prepared = con.Prepare( "INSERT INTO dates VALUES ($1)"); | 
|---|
| 176 | REQUIRE_NO_FAIL(prepared->Execute(Value::DATE(1992, 1, 3))); | 
|---|
| 177 |  | 
|---|
| 178 | result = con.Query( "SELECT * FROM dates"); | 
|---|
| 179 | REQUIRE(CHECK_COLUMN(result, 0, {Value::DATE(1992, 1, 3)})); | 
|---|
| 180 | } | 
|---|
| 181 |  | 
|---|
| 182 | TEST_CASE( "PREPARE for DELETE/UPDATE", "[prepared]") { | 
|---|
| 183 | unique_ptr<QueryResult> result; | 
|---|
| 184 | DuckDB db(nullptr); | 
|---|
| 185 | Connection con(db); | 
|---|
| 186 |  | 
|---|
| 187 | // DELETE | 
|---|
| 188 | REQUIRE_NO_FAIL(con.Query( "CREATE TABLE b (i TINYINT)")); | 
|---|
| 189 | REQUIRE_NO_FAIL(con.Query( "INSERT INTO b VALUES (1), (2), (3), (4), (5)")); | 
|---|
| 190 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS DELETE FROM b WHERE i=$1")); | 
|---|
| 191 |  | 
|---|
| 192 | result = con.Query( "SELECT * FROM b ORDER BY 1"); | 
|---|
| 193 | REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3, 4, 5})); | 
|---|
| 194 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s1(3)")); | 
|---|
| 195 |  | 
|---|
| 196 | result = con.Query( "SELECT * FROM b ORDER BY 1"); | 
|---|
| 197 | REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 4, 5})); | 
|---|
| 198 |  | 
|---|
| 199 | // cannot drop table now | 
|---|
| 200 | REQUIRE_FAIL(con.Query( "DROP TABLE b")); | 
|---|
| 201 | // but we can with cascade | 
|---|
| 202 | REQUIRE_NO_FAIL(con.Query( "DROP TABLE b CASCADE")); | 
|---|
| 203 |  | 
|---|
| 204 | // UPDATE | 
|---|
| 205 | REQUIRE_NO_FAIL(con.Query( "CREATE TABLE b (i TINYINT)")); | 
|---|
| 206 | REQUIRE_NO_FAIL(con.Query( "INSERT INTO b VALUES (1), (2), (3), (4), (5)")); | 
|---|
| 207 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS UPDATE b SET i=$1 WHERE i=$2")); | 
|---|
| 208 |  | 
|---|
| 209 | result = con.Query( "SELECT * FROM b ORDER BY 1"); | 
|---|
| 210 | REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3, 4, 5})); | 
|---|
| 211 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s1(6, 3)")); | 
|---|
| 212 |  | 
|---|
| 213 | result = con.Query( "SELECT * FROM b ORDER BY 1"); | 
|---|
| 214 | REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 4, 5, 6})); | 
|---|
| 215 |  | 
|---|
| 216 | // cannot drop table now | 
|---|
| 217 | REQUIRE_FAIL(con.Query( "DROP TABLE b")); | 
|---|
| 218 | // but we can with cascade | 
|---|
| 219 | REQUIRE_NO_FAIL(con.Query( "DROP TABLE b CASCADE")); | 
|---|
| 220 | } | 
|---|
| 221 |  | 
|---|
| 222 | TEST_CASE( "PREPARE for UPDATE", "[prepared]") { | 
|---|
| 223 | unique_ptr<QueryResult> result; | 
|---|
| 224 | DuckDB db(nullptr); | 
|---|
| 225 | Connection con(db); | 
|---|
| 226 |  | 
|---|
| 227 | REQUIRE_NO_FAIL(con.Query( "CREATE TABLE b (i TINYINT)")); | 
|---|
| 228 | REQUIRE_NO_FAIL(con.Query( "INSERT INTO b VALUES (1), (2), (3), (4), (5)")); | 
|---|
| 229 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS DELETE FROM b WHERE i=$1")); | 
|---|
| 230 |  | 
|---|
| 231 | result = con.Query( "SELECT * FROM b ORDER BY 1"); | 
|---|
| 232 | REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3, 4, 5})); | 
|---|
| 233 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s1(3)")); | 
|---|
| 234 |  | 
|---|
| 235 | result = con.Query( "SELECT * FROM b ORDER BY 1"); | 
|---|
| 236 | REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 4, 5})); | 
|---|
| 237 |  | 
|---|
| 238 | // cannot drop table now | 
|---|
| 239 | REQUIRE_FAIL(con.Query( "DROP TABLE b")); | 
|---|
| 240 | // but we can with cascade | 
|---|
| 241 | REQUIRE_NO_FAIL(con.Query( "DROP TABLE b CASCADE")); | 
|---|
| 242 | } | 
|---|
| 243 |  | 
|---|
| 244 | TEST_CASE( "PREPARE many types for INSERT", "[prepared]") { | 
|---|
| 245 | unique_ptr<QueryResult> result; | 
|---|
| 246 | DuckDB db(nullptr); | 
|---|
| 247 | Connection con(db); | 
|---|
| 248 |  | 
|---|
| 249 | // prepare different types in insert | 
|---|
| 250 | REQUIRE_NO_FAIL(con.Query( | 
|---|
| 251 | "CREATE TABLE test(a TINYINT, b SMALLINT, c INTEGER, d BIGINT, e REAL, f DOUBLE, g DATE, h VARCHAR)")); | 
|---|
| 252 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS INSERT INTO test VALUES ($1,$2,$3,$4,$5,$6,$7,$8);")); | 
|---|
| 253 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s1(1,2,3,4,1.5,2.5,'1992-10-20', 'hello world');")); | 
|---|
| 254 | result = con.Query( "SELECT * FROM test"); | 
|---|
| 255 | REQUIRE(CHECK_COLUMN(result, 0, {1})); | 
|---|
| 256 | REQUIRE(CHECK_COLUMN(result, 1, {2})); | 
|---|
| 257 | REQUIRE(CHECK_COLUMN(result, 2, {3})); | 
|---|
| 258 | REQUIRE(CHECK_COLUMN(result, 3, {4})); | 
|---|
| 259 | REQUIRE(CHECK_COLUMN(result, 4, {(float)1.5})); | 
|---|
| 260 | REQUIRE(CHECK_COLUMN(result, 5, {2.5})); | 
|---|
| 261 | REQUIRE(CHECK_COLUMN(result, 6, {Value::DATE(1992, 10, 20)})); | 
|---|
| 262 | REQUIRE(CHECK_COLUMN(result, 7, { "hello world"})); | 
|---|
| 263 | } | 
|---|
| 264 |  | 
|---|
| 265 | TEST_CASE( "PREPARE and DROPping tables", "[prepared]") { | 
|---|
| 266 | unique_ptr<QueryResult> result; | 
|---|
| 267 | DuckDB db(nullptr); | 
|---|
| 268 | Connection con1(db); | 
|---|
| 269 | Connection con2(db); | 
|---|
| 270 |  | 
|---|
| 271 | REQUIRE_NO_FAIL(con1.Query( "CREATE TABLE a (i TINYINT)")); | 
|---|
| 272 | REQUIRE_NO_FAIL(con2.Query( "PREPARE p1 AS SELECT * FROM a")); | 
|---|
| 273 |  | 
|---|
| 274 | REQUIRE_NO_FAIL(con2.Query( "EXECUTE p1")); | 
|---|
| 275 |  | 
|---|
| 276 | // only the conn which did the prepare can execute | 
|---|
| 277 | REQUIRE_FAIL(con1.Query( "EXECUTE p1")); | 
|---|
| 278 |  | 
|---|
| 279 | // but someone else cannot drop the table | 
|---|
| 280 | REQUIRE_FAIL(con1.Query( "DROP TABLE a")); | 
|---|
| 281 |  | 
|---|
| 282 | // but when we take the statement away | 
|---|
| 283 | REQUIRE_NO_FAIL(con2.Query( "DEALLOCATE p1")); | 
|---|
| 284 |  | 
|---|
| 285 | // we can drop | 
|---|
| 286 | REQUIRE_NO_FAIL(con1.Query( "DROP TABLE a")); | 
|---|
| 287 | } | 
|---|
| 288 |  | 
|---|
| 289 | TEST_CASE( "PREPARE and WAL", "[prepared][.]") { | 
|---|
| 290 | unique_ptr<QueryResult> result; | 
|---|
| 291 | auto prepare_database = TestCreatePath( "prepare_test"); | 
|---|
| 292 |  | 
|---|
| 293 | // make sure the database does not exist | 
|---|
| 294 | DeleteDatabase(prepare_database); | 
|---|
| 295 | { | 
|---|
| 296 | // create a database and insert values | 
|---|
| 297 | DuckDB db(prepare_database); | 
|---|
| 298 | Connection con(db); | 
|---|
| 299 | REQUIRE_NO_FAIL(con.Query( "CREATE TABLE t (a INTEGER)")); | 
|---|
| 300 | REQUIRE_NO_FAIL(con.Query( "PREPARE p1 AS INSERT INTO t VALUES ($1)")); | 
|---|
| 301 | REQUIRE_NO_FAIL(con.Query( "EXECUTE p1(42)")); | 
|---|
| 302 | REQUIRE_NO_FAIL(con.Query( "EXECUTE p1(43)")); | 
|---|
| 303 | REQUIRE_NO_FAIL(con.Query( "DEALLOCATE p1")); | 
|---|
| 304 | result = con.Query( "SELECT a FROM t"); | 
|---|
| 305 | REQUIRE(CHECK_COLUMN(result, 0, {42, 43})); | 
|---|
| 306 | } | 
|---|
| 307 | { | 
|---|
| 308 | DuckDB db(prepare_database); | 
|---|
| 309 | Connection con(db); | 
|---|
| 310 |  | 
|---|
| 311 | result = con.Query( "SELECT a FROM t"); | 
|---|
| 312 | REQUIRE(CHECK_COLUMN(result, 0, {42, 43})); | 
|---|
| 313 |  | 
|---|
| 314 | // unhelpfully use the same statement name again, it should be available, but do nothing with it | 
|---|
| 315 | REQUIRE_NO_FAIL(con.Query( "PREPARE p1 AS DELETE FROM t WHERE a=$1")); | 
|---|
| 316 | } | 
|---|
| 317 | // reload the database from disk | 
|---|
| 318 | { | 
|---|
| 319 | DuckDB db(prepare_database); | 
|---|
| 320 | Connection con(db); | 
|---|
| 321 | REQUIRE_NO_FAIL(con.Query( "PREPARE p1 AS DELETE FROM t WHERE a=$1")); | 
|---|
| 322 | REQUIRE_NO_FAIL(con.Query( "EXECUTE p1(43)")); | 
|---|
| 323 |  | 
|---|
| 324 | result = con.Query( "SELECT a FROM t"); | 
|---|
| 325 | REQUIRE(CHECK_COLUMN(result, 0, {42})); | 
|---|
| 326 | } | 
|---|
| 327 | // reload again | 
|---|
| 328 |  | 
|---|
| 329 | { | 
|---|
| 330 | DuckDB db(prepare_database); | 
|---|
| 331 | Connection con(db); | 
|---|
| 332 |  | 
|---|
| 333 | result = con.Query( "SELECT a FROM t"); | 
|---|
| 334 | REQUIRE(CHECK_COLUMN(result, 0, {42})); | 
|---|
| 335 | } | 
|---|
| 336 |  | 
|---|
| 337 | { | 
|---|
| 338 | DuckDB db(prepare_database); | 
|---|
| 339 | Connection con(db); | 
|---|
| 340 |  | 
|---|
| 341 | result = con.Query( "SELECT a FROM t"); | 
|---|
| 342 | REQUIRE(CHECK_COLUMN(result, 0, {42})); | 
|---|
| 343 |  | 
|---|
| 344 | REQUIRE_NO_FAIL(con.Query( "PREPARE p1 AS UPDATE t SET a = $1")); | 
|---|
| 345 | REQUIRE_NO_FAIL(con.Query( "EXECUTE p1(43)")); | 
|---|
| 346 |  | 
|---|
| 347 | result = con.Query( "SELECT a FROM t"); | 
|---|
| 348 | REQUIRE(CHECK_COLUMN(result, 0, {43})); | 
|---|
| 349 | } | 
|---|
| 350 | for (idx_t i = 0; i < 2; i++) { | 
|---|
| 351 | DuckDB db(prepare_database); | 
|---|
| 352 | Connection con(db); | 
|---|
| 353 |  | 
|---|
| 354 | result = con.Query( "SELECT a FROM t"); | 
|---|
| 355 | REQUIRE(CHECK_COLUMN(result, 0, {43})); | 
|---|
| 356 | } | 
|---|
| 357 | DeleteDatabase(prepare_database); | 
|---|
| 358 | } | 
|---|
| 359 |  | 
|---|
| 360 | TEST_CASE( "PREPARE with NULL", "[prepared]") { | 
|---|
| 361 | unique_ptr<QueryResult> result; | 
|---|
| 362 | DuckDB db(nullptr); | 
|---|
| 363 | Connection con(db); | 
|---|
| 364 |  | 
|---|
| 365 | REQUIRE_NO_FAIL(con.Query( "CREATE TABLE b (i TINYINT)")); | 
|---|
| 366 | REQUIRE_NO_FAIL(con.Query( "PREPARE s1 AS INSERT INTO b VALUES ($1)")); | 
|---|
| 367 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s1 (NULL)")); | 
|---|
| 368 |  | 
|---|
| 369 | result = con.Query( "SELECT i FROM b"); | 
|---|
| 370 | REQUIRE(CHECK_COLUMN(result, 0, {Value()})); | 
|---|
| 371 |  | 
|---|
| 372 | REQUIRE_NO_FAIL(con.Query( "PREPARE s2 AS UPDATE b SET i=$1")); | 
|---|
| 373 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s2 (NULL)")); | 
|---|
| 374 |  | 
|---|
| 375 | result = con.Query( "SELECT i FROM b"); | 
|---|
| 376 | REQUIRE(CHECK_COLUMN(result, 0, {Value()})); | 
|---|
| 377 |  | 
|---|
| 378 | REQUIRE_NO_FAIL(con.Query( "PREPARE s3 AS DELETE FROM b WHERE i=$1")); | 
|---|
| 379 | REQUIRE_NO_FAIL(con.Query( "EXECUTE s3 (NULL)")); | 
|---|
| 380 |  | 
|---|
| 381 | result = con.Query( "SELECT i FROM b"); | 
|---|
| 382 | REQUIRE(CHECK_COLUMN(result, 0, {Value()})); | 
|---|
| 383 | } | 
|---|
| 384 |  | 
|---|
| 385 | TEST_CASE( "PREPARE multiple statements", "[prepared]") { | 
|---|
| 386 | unique_ptr<QueryResult> result; | 
|---|
| 387 | DuckDB db(nullptr); | 
|---|
| 388 | Connection con(db); | 
|---|
| 389 |  | 
|---|
| 390 | string query = "SELECT $1::INTEGER; SELECT $1::INTEGER;"; | 
|---|
| 391 | // cannot prepare multiple statements like this | 
|---|
| 392 | auto prepared = con.Prepare(query); | 
|---|
| 393 | REQUIRE(!prepared->success); | 
|---|
| 394 | // we can use ExtractStatements to execute the individual statements though | 
|---|
| 395 | auto statements = con.ExtractStatements(query); | 
|---|
| 396 | for (auto &statement : statements) { | 
|---|
| 397 | string stmt = query.substr(statement->stmt_location, statement->stmt_length); | 
|---|
| 398 | prepared = con.Prepare(stmt); | 
|---|
| 399 | REQUIRE(prepared->success); | 
|---|
| 400 |  | 
|---|
| 401 | result = prepared->Execute(1); | 
|---|
| 402 | REQUIRE(CHECK_COLUMN(result, 0, {1})); | 
|---|
| 403 | } | 
|---|
| 404 | } | 
|---|
| 405 |  | 
|---|
| 406 | static unique_ptr<QueryResult> TestExecutePrepared(Connection &con, string query) { | 
|---|
| 407 | auto prepared = con.Prepare(query); | 
|---|
| 408 | return prepared->Execute(); | 
|---|
| 409 | } | 
|---|
| 410 |  | 
|---|
| 411 | TEST_CASE( "Prepare all types of statements", "[prepared]") { | 
|---|
| 412 | unique_ptr<QueryResult> result; | 
|---|
| 413 | DuckDB db(nullptr); | 
|---|
| 414 | Connection con(db); | 
|---|
| 415 |  | 
|---|
| 416 | string csv_path = TestCreatePath( "prepared_files"); | 
|---|
| 417 | if (db.file_system->DirectoryExists(csv_path)) { | 
|---|
| 418 | db.file_system->RemoveDirectory(csv_path); | 
|---|
| 419 | } | 
|---|
| 420 |  | 
|---|
| 421 | // TRANSACTION | 
|---|
| 422 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "BEGIN TRANSACTION")); | 
|---|
| 423 | // SELECT | 
|---|
| 424 | result = TestExecutePrepared(con, "SELECT 42"); | 
|---|
| 425 | REQUIRE(CHECK_COLUMN(result, 0, {42})); | 
|---|
| 426 | // CREATE_SCHEMA | 
|---|
| 427 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "CREATE SCHEMA test")); | 
|---|
| 428 | // CREATE_TABLE | 
|---|
| 429 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "CREATE TABLE test.a(i INTEGER)")); | 
|---|
| 430 | // CREATE_TABLE | 
|---|
| 431 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "CREATE TABLE b(i INTEGER)")); | 
|---|
| 432 | // CREATE_INDEX | 
|---|
| 433 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "CREATE INDEX i_index ON test.a(i)")); | 
|---|
| 434 | // CREATE_VIEW | 
|---|
| 435 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "CREATE VIEW v1 AS SELECT * FROM test.a WHERE i=2")); | 
|---|
| 436 | // CREATE_SEQUENCE | 
|---|
| 437 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "CREATE SEQUENCE seq")); | 
|---|
| 438 | // PRAGMA | 
|---|
| 439 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "PRAGMA table_info('b')")); | 
|---|
| 440 | // EXPLAIN | 
|---|
| 441 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "EXPLAIN SELECT 42")); | 
|---|
| 442 | // COPY | 
|---|
| 443 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "COPY test.a TO '"+ csv_path + "'")); | 
|---|
| 444 | // INSERT | 
|---|
| 445 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "INSERT INTO test.a VALUES (1), (2), (3)")); | 
|---|
| 446 | // UPDATE | 
|---|
| 447 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "UPDATE test.a SET i=i+1")); | 
|---|
| 448 | // DELETE | 
|---|
| 449 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "DELETE FROM test.a WHERE i<4")); | 
|---|
| 450 | // PREPARE | 
|---|
| 451 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "PREPARE p1 AS SELECT * FROM test.a")); | 
|---|
| 452 | // EXECUTE | 
|---|
| 453 | result = TestExecutePrepared(con, "EXECUTE p1"); | 
|---|
| 454 | REQUIRE(CHECK_COLUMN(result, 0, {4})); | 
|---|
| 455 | // DROP | 
|---|
| 456 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "DROP SEQUENCE seq")); | 
|---|
| 457 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "DROP VIEW v1")); | 
|---|
| 458 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "DROP TABLE test.a CASCADE")); | 
|---|
| 459 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "DROP SCHEMA test CASCADE")); | 
|---|
| 460 |  | 
|---|
| 461 | // TRANSACTION | 
|---|
| 462 | REQUIRE_NO_FAIL(TestExecutePrepared(con, "COMMIT")); | 
|---|
| 463 | } | 
|---|
| 464 |  | 
|---|