130 lines
5.0 KiB
C++
130 lines
5.0 KiB
C++
#include "catch.hpp"
|
|
#include "duckdb/common/file_system.hpp"
|
|
#include "test_helpers.hpp"
|
|
|
|
using namespace duckdb;
|
|
using namespace std;
|
|
|
|
TEST_CASE("Test connection using a read only database", "[readonly]") {
|
|
auto dbdir = TestCreatePath("read_only_test");
|
|
duckdb::unique_ptr<DuckDB> db, db2;
|
|
duckdb::unique_ptr<Connection> con;
|
|
// make sure the database does not exist
|
|
DeleteDatabase(dbdir);
|
|
|
|
DBConfig readonly_config;
|
|
readonly_config.options.use_temporary_directory = false;
|
|
readonly_config.options.access_mode = AccessMode::READ_ONLY;
|
|
|
|
// cannot create read-only memory database
|
|
REQUIRE_THROWS(db = make_uniq<DuckDB>(nullptr, &readonly_config));
|
|
// cannot create a read-only database in a new directory
|
|
REQUIRE_THROWS(db = make_uniq<DuckDB>(dbdir, &readonly_config));
|
|
|
|
// create the database file and initialize it with data
|
|
db = make_uniq<DuckDB>(dbdir);
|
|
con = make_uniq<Connection>(*db);
|
|
REQUIRE_NO_FAIL(con->Query("CREATE TABLE integers(i INTEGER)"));
|
|
REQUIRE_NO_FAIL(con->Query("INSERT INTO integers VALUES (1), (2), (3), (4), (5)"));
|
|
con.reset();
|
|
db.reset();
|
|
|
|
// now connect in read-only mode
|
|
REQUIRE_NOTHROW(db = make_uniq<DuckDB>(dbdir, &readonly_config));
|
|
con = make_uniq<Connection>(*db);
|
|
|
|
// we can query the database
|
|
auto result = con->Query("SELECT * FROM integers ORDER BY i");
|
|
REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3, 4, 5}));
|
|
// however, we can't perform DDL statements
|
|
REQUIRE_FAIL(con->Query("CREATE TABLE integers2(i INTEGER)"));
|
|
REQUIRE_FAIL(con->Query("ALTER TABLE integers RENAME COLUMN i TO k"));
|
|
REQUIRE_FAIL(con->Query("DROP TABLE integers"));
|
|
REQUIRE_FAIL(con->Query("CREATE SEQUENCE seq"));
|
|
REQUIRE_FAIL(con->Query("CREATE VIEW v1 AS SELECT * FROM integers"));
|
|
// neither can we insert/update/delete data
|
|
REQUIRE_FAIL(con->Query("INSERT INTO integers VALUES (3)"));
|
|
REQUIRE_FAIL(con->Query("UPDATE integers SET i=5"));
|
|
REQUIRE_FAIL(con->Query("DELETE FROM integers"));
|
|
// we can run explain queries
|
|
REQUIRE_NO_FAIL(con->Query("EXPLAIN SELECT * FROM integers"));
|
|
// and run prepared statements
|
|
REQUIRE_NO_FAIL(con->Query("PREPARE v1 AS SELECT * FROM integers"));
|
|
REQUIRE_NO_FAIL(con->Query("EXECUTE v1"));
|
|
REQUIRE_NO_FAIL(con->Query("DEALLOCATE v1"));
|
|
// we can also prepare a DDL/update statement
|
|
REQUIRE_NO_FAIL(con->Query("PREPARE v1 AS INSERT INTO integers VALUES ($1)"));
|
|
// however, executing it fails then!
|
|
REQUIRE_FAIL(con->Query("EXECUTE v1(3)"));
|
|
|
|
// we can create, alter and query temporary tables however
|
|
REQUIRE_NO_FAIL(con->Query("CREATE TEMPORARY TABLE integers2(i INTEGER)"));
|
|
REQUIRE_NO_FAIL(con->Query("INSERT INTO integers2 VALUES (1), (2), (3), (4), (5)"));
|
|
REQUIRE_NO_FAIL(con->Query("UPDATE integers2 SET i=i+1"));
|
|
result = con->Query("DELETE FROM integers2 WHERE i=3");
|
|
REQUIRE(CHECK_COLUMN(result, 0, {1}));
|
|
|
|
REQUIRE_NO_FAIL(con->Query("ALTER TABLE integers2 RENAME COLUMN i TO k"));
|
|
result = con->Query("SELECT k FROM integers2 ORDER BY 1");
|
|
REQUIRE(CHECK_COLUMN(result, 0, {2, 4, 5, 6}));
|
|
REQUIRE_NO_FAIL(con->Query("DROP TABLE integers2"));
|
|
|
|
// also temporary views and sequences
|
|
REQUIRE_NO_FAIL(con->Query("CREATE TEMPORARY SEQUENCE seq"));
|
|
result = con->Query("SELECT nextval('seq')");
|
|
REQUIRE(CHECK_COLUMN(result, 0, {1}));
|
|
REQUIRE_NO_FAIL(con->Query("DROP SEQUENCE seq"));
|
|
|
|
REQUIRE_NO_FAIL(con->Query("CREATE TEMPORARY VIEW v1 AS SELECT 42"));
|
|
result = con->Query("SELECT * FROM v1");
|
|
REQUIRE(CHECK_COLUMN(result, 0, {42}));
|
|
REQUIRE_NO_FAIL(con->Query("DROP VIEW v1"));
|
|
|
|
con.reset();
|
|
db.reset();
|
|
// FIXME: these tests currently don't work as we don't do any locking of the database directory
|
|
// this should be fixed with the new storage
|
|
// we can connect multiple read only databases to the same dbdir
|
|
// REQUIRE_NOTHROW(db = make_uniq<DuckDB>(dbdir, true));
|
|
// REQUIRE_NOTHROW(db2 = make_uniq<DuckDB>(dbdir, true));
|
|
// db.reset();
|
|
// db2.reset();
|
|
|
|
// // however, if there is read-only database, we can't connect a read-write database
|
|
// REQUIRE_NOTHROW(db = make_uniq<DuckDB>(dbdir, true));
|
|
// REQUIRE_THROWS(db2 = make_uniq<DuckDB>(dbdir));
|
|
// db.reset();
|
|
// db2.reset();
|
|
|
|
// // if we add a read-write database first, we can't add a reading database afterwards either
|
|
// REQUIRE_NOTHROW(db = make_uniq<DuckDB>(dbdir));
|
|
// REQUIRE_THROWS(db2 = make_uniq<DuckDB>(dbdir, true));
|
|
// db.reset();
|
|
// db2.reset();
|
|
DeleteDatabase(dbdir);
|
|
}
|
|
|
|
TEST_CASE("Test view creation using a read only database", "[readonly]") {
|
|
auto dbdir = TestCreatePath("read_only_view_test");
|
|
duckdb::unique_ptr<DuckDB> db;
|
|
duckdb::unique_ptr<Connection> con;
|
|
// make sure the database does not exist
|
|
DeleteDatabase(dbdir);
|
|
|
|
DBConfig readonly_config;
|
|
readonly_config.options.use_temporary_directory = false;
|
|
readonly_config.options.access_mode = AccessMode::READ_ONLY;
|
|
|
|
// create db in first place
|
|
{ auto db_rw = DuckDB(dbdir); }
|
|
db = make_uniq<DuckDB>(dbdir, &readonly_config);
|
|
// create the database file and initialize it with data
|
|
con = make_uniq<Connection>(*db);
|
|
|
|
REQUIRE_NOTHROW(con->TableFunction("duckdb_tables")->CreateView("boo", true, true));
|
|
con.reset();
|
|
db.reset();
|
|
|
|
DeleteDatabase(dbdir);
|
|
}
|