1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
#include "catch.hpp"
#include "test_helpers.hpp"
using namespace duckdb;
using namespace std;
static void CreateSimpleTable(Connection &con) {
REQUIRE_NO_FAIL(con.Query("CREATE TABLE a (i TINYINT)"));
REQUIRE_NO_FAIL(con.Query("INSERT INTO a VALUES (11), (12), (13)"));
}
static void ModifySimpleTable(Connection &con) {
REQUIRE_NO_FAIL(con.Query("INSERT INTO a VALUES (14)"));
REQUIRE_NO_FAIL(con.Query("DELETE FROM a where i=12"));
}
static void CheckSimpleQuery(Connection &con) {
auto statements = con.ExtractStatements("SELECT COUNT(*) FROM a WHERE i=12");
REQUIRE(statements.size() == 1);
duckdb::vector<duckdb::Value> values = {Value(12)};
auto pending_result = con.PendingQuery("SELECT COUNT(*) FROM a WHERE i=?", values, true);
if (pending_result->HasError()) {
printf("%s\n", pending_result->GetError().c_str());
}
REQUIRE(!pending_result->HasError());
auto result = pending_result->Execute();
REQUIRE(CHECK_COLUMN(result, 0, {1}));
}
static void CheckCatalogErrorQuery(Connection &con) {
duckdb::vector<Value> values = {Value(12)};
auto pending_result = con.PendingQuery("SELECT COUNT(*) FROM b WHERE i=?", values, true);
REQUIRE((pending_result->HasError() && pending_result->GetErrorType() == ExceptionType::CATALOG));
}
static void CheckConversionErrorQuery(Connection &con) {
// Check query with invalid prepared value
duckdb::vector<Value> values = {Value("fawakaaniffoo")};
auto pending_result = con.PendingQuery("SELECT COUNT(*) FROM a WHERE i=?", values, true);
REQUIRE(!pending_result->HasError());
auto result = pending_result->Execute();
REQUIRE((result->HasError() && result->GetErrorType() == ExceptionType::CONVERSION));
}
static void CheckSimpleQueryAfterModification(Connection &con) {
duckdb::vector<Value> values = {Value(14)};
auto pending_result = con.PendingQuery("SELECT COUNT(*) FROM a WHERE i=?", values, true);
REQUIRE(!pending_result->HasError());
auto result = pending_result->Execute();
REQUIRE(CHECK_COLUMN(result, 0, {1}));
}
TEST_CASE("Pending Query with Parameters", "[api]") {
DuckDB db(nullptr);
Connection con(db);
con.EnableQueryVerification();
CreateSimpleTable(con);
CheckSimpleQuery(con);
CheckSimpleQuery(con);
}
TEST_CASE("Pending Query with Parameters Catalog Error", "[api]") {
DuckDB db(nullptr);
Connection con(db);
con.EnableQueryVerification();
CreateSimpleTable(con);
CheckCatalogErrorQuery(con);
// Verify things are still sane
CheckSimpleQuery(con);
}
TEST_CASE("Pending Query with Parameters Type Conversion Error", "[api]") {
DuckDB db(nullptr);
Connection con(db);
con.EnableQueryVerification();
CreateSimpleTable(con);
CheckConversionErrorQuery(con);
// Verify things are still sane
CheckSimpleQuery(con);
}
TEST_CASE("Pending Query with Parameters with transactions", "[api]") {
DuckDB db(nullptr);
Connection con1(db);
Connection con2(db);
duckdb::vector<Value> empty_values = {};
con1.EnableQueryVerification();
CreateSimpleTable(con1);
// CheckConversionErrorQuery(con1);
// Begin a transaction in the PrepareAndExecute
auto pending_result1 = con1.PendingQuery("BEGIN TRANSACTION", empty_values, true);
if (pending_result1->HasError()) {
printf("%s\n", pending_result1->GetError().c_str());
}
REQUIRE(!pending_result1->HasError());
auto result1 = pending_result1->Execute();
REQUIRE(!result1->HasError());
CheckSimpleQuery(con1);
// Modify table on other connection, leaving transaction open
con2.BeginTransaction();
ModifySimpleTable(con2);
CheckSimpleQueryAfterModification(con2);
// con1 sees nothing: both transactions are open
CheckSimpleQuery(con1);
con2.Commit();
// con1 still sees nothing: its transaction was started before con2's
CheckSimpleQuery(con1);
// con 1 commits
auto pending_result2 = con1.PendingQuery("COMMIT", empty_values, true);
auto result2 = pending_result2->Execute();
REQUIRE(!result2->HasError());
// now con1 should see changes from con2
CheckSimpleQueryAfterModification(con1);
CheckSimpleQueryAfterModification(con2);
}
|