reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

database.cpp (6122B)


      1 #include "helper.hpp"
      2 
      3 #include <database.hpp>
      4 
      5 #include <errors.hpp>
      6 
      7 #include <sqlite3.h>
      8 
      9 static const char *M = "[database]";
     10 
     11 TEST_CASE("open bad sqlite file path", M) {
     12   try {
     13     Database db("/");
     14     FAIL();
     15   }
     16   catch(const reapack_error &e) {
     17     REQUIRE(std::string{e.what()} == "unable to open database file");
     18   }
     19 }
     20 
     21 TEST_CASE("execute invalid sql", M) {
     22   Database db;
     23 
     24   try {
     25     db.exec("WHERE");
     26     FAIL();
     27   }
     28   catch(const reapack_error &e) {
     29     REQUIRE(std::string{e.what()} == "near \"WHERE\": syntax error");
     30   }
     31 }
     32 
     33 TEST_CASE("prepare invalid sql", M) {
     34   Database db;
     35 
     36   try {
     37     db.prepare("WHERE");
     38     FAIL();
     39   }
     40   catch(const reapack_error &e) {
     41     REQUIRE(std::string{e.what()} == "near \"WHERE\": syntax error");
     42   }
     43 }
     44 
     45 TEST_CASE("get rows from prepared statement", M) {
     46   Database db;
     47   db.exec(
     48     "CREATE TABLE test (value TEXT NOT NULL);"
     49     "INSERT INTO test VALUES (\"hello\");"
     50     "INSERT INTO test VALUES (\"世界\");"
     51   );
     52 
     53   std::vector<std::string> values;
     54 
     55   Statement *stmt = db.prepare("SELECT value FROM test");
     56 
     57   SECTION("continue") {
     58     stmt->exec([&] {
     59       values.push_back(stmt->stringColumn(0));
     60       return true;
     61     });
     62 
     63     REQUIRE(values.size() == 2);
     64     REQUIRE(values[0] == "hello");
     65     REQUIRE(values[1] == "世界");
     66   }
     67 
     68   SECTION("abort") {
     69     stmt->exec([&] {
     70       values.push_back(stmt->stringColumn(0));
     71       return false;
     72     });
     73 
     74     REQUIRE(values.size() == 1);
     75     REQUIRE(values[0] == "hello");
     76   }
     77 }
     78 
     79 TEST_CASE("bind values and clear", M) {
     80   Database db;
     81   db.exec("CREATE TABLE test (value TEXT NOT NULL)");
     82 
     83   Statement *stmt = db.prepare("INSERT INTO test VALUES (?)");
     84   stmt->bind(1, "hello");
     85   stmt->exec();
     86 
     87   try {
     88     stmt->exec();
     89     FAIL("bindings not cleared");
     90   }
     91   catch(const reapack_error &e) {
     92     REQUIRE(std::string{e.what()} == "NOT NULL constraint failed: test.value");
     93   }
     94 }
     95 
     96 TEST_CASE("database version", M) {
     97   Database db;
     98   REQUIRE(db.version().major == 0);
     99   REQUIRE(db.version().minor == 0);
    100   REQUIRE_FALSE(db.version());
    101   REQUIRE_FALSE(db.version() < (Database::Version{0, 0}));
    102   REQUIRE(db.version() < (Database::Version{0, 1}));
    103   REQUIRE(db.version() < (Database::Version{1, 0}));
    104 
    105   db.setVersion({0, 1});
    106   REQUIRE(db.version().major == 0);
    107   REQUIRE(db.version().minor == 1);
    108   REQUIRE(db.version());
    109 
    110   db.setVersion({1, 2});
    111   REQUIRE(db.version() < (Database::Version{2, 3}));
    112   REQUIRE_FALSE(db.version() < (Database::Version{1, 1}));
    113   REQUIRE_FALSE(db.version() < (Database::Version{0, 3}));
    114 
    115   db.setVersion({32767, 32767});
    116   REQUIRE(db.version().major == 32767);
    117   REQUIRE(db.version().minor == 32767);
    118 }
    119 
    120 TEST_CASE("foreign keys", M) {
    121   Database db;
    122   db.exec(
    123     "CREATE TABLE a (id INTEGER PRIMARY KEY);"
    124 
    125     "CREATE TABLE b ("
    126     "  two INTEGER NOT NULL,"
    127     "  FOREIGN KEY(two) REFERENCES a(id)"
    128     ");"
    129 
    130     "INSERT INTO a VALUES(NULL);"
    131     "INSERT INTO b VALUES(1);"
    132   );
    133 
    134   try {
    135     db.exec("DELETE FROM a");
    136     FAIL("foreign keys checks are disabled");
    137   }
    138   catch(const reapack_error &) {}
    139 }
    140 
    141 TEST_CASE("last insert id", M) {
    142   Database db;
    143   db.exec("CREATE TABLE a(text TEXT)");
    144 
    145   Statement *insert = db.prepare("INSERT INTO a VALUES(NULL)");
    146 
    147   REQUIRE(db.lastInsertId() == 0);
    148 
    149   insert->exec();
    150   REQUIRE(db.lastInsertId() == 1);
    151 
    152   insert->exec();
    153   REQUIRE(db.lastInsertId() == 2);
    154 }
    155 
    156 TEST_CASE("bind temporary strings", M) {
    157   Database db;
    158   db.exec("CREATE TABLE a(text TEXT NOT NULL)");
    159 
    160   Statement *insert = db.prepare("INSERT INTO a VALUES(?)");
    161 
    162   std::string str("hello");
    163   insert->bind(1, str);
    164   str = "world";
    165 
    166   insert->exec();
    167 
    168   std::string got;
    169   Statement *select = db.prepare("SELECT text FROM a LIMIT 1");
    170   select->exec([&] {
    171     got = select->stringColumn(0);
    172     return false;
    173   });
    174 
    175   REQUIRE(got == "hello");
    176 }
    177 
    178 TEST_CASE("get integers from sqlite", M) {
    179   Database db;
    180   db.exec("CREATE TABLE a(test INTEGER NOT NULL)");
    181 
    182   Statement *insert = db.prepare("INSERT INTO a VALUES(?)");
    183 
    184   insert->bind(1, 2147483647);
    185   insert->exec();
    186   insert->bind(1, 4294967295);
    187   insert->exec();
    188 
    189   std::vector<sqlite3_int64> signedVals;
    190   Statement *select = db.prepare("SELECT test FROM a");
    191   select->exec([&] {
    192     signedVals.push_back(select->intColumn(0));
    193     return true;
    194   });
    195 
    196   CHECK(signedVals.size() == 2);
    197   REQUIRE(signedVals[0] == 2147483647);
    198   REQUIRE(signedVals[1] == 4294967295);
    199 }
    200 
    201 TEST_CASE("sqlite error code", M) {
    202   Database db;
    203   db.exec("CREATE TABLE a(b INTEGER UNIQUE); INSERT INTO a VALUES(1)");
    204   REQUIRE(db.errorCode() == SQLITE_OK);
    205 
    206   try {
    207     db.exec("INSERT INTO a VALUES(1)");
    208   }
    209   catch(const reapack_error &) {}
    210 
    211   REQUIRE(db.errorCode() == SQLITE_CONSTRAINT);
    212 }
    213 
    214 TEST_CASE("invalid string column", M) {
    215   Database db;
    216   db.exec("CREATE TABLE a(text TEXT NOT NULL)");
    217 
    218   Statement *insert = db.prepare("INSERT INTO a VALUES(?)");
    219   insert->bind(1, "hello");
    220   insert->exec();
    221 
    222   Statement *select = db.prepare("SELECT text FROM a LIMIT 1");
    223   select->exec([&] {
    224     REQUIRE(select->stringColumn(4242).empty()); // don't crash!
    225     return false;
    226   });
    227 }
    228 
    229 TEST_CASE("database transaction locking", M) {
    230   Database db;
    231   db.begin();
    232 
    233   try {
    234     db.begin();
    235     FAIL("created a transaction within a transaction");
    236   }
    237   catch(const reapack_error &) {}
    238 
    239   db.commit();
    240   db.begin();
    241 }
    242 
    243 TEST_CASE("save points", M) {
    244   Database db;
    245   db.exec("CREATE TABLE test (value INTEGER NOT NULL);");
    246 
    247   Statement *insert = db.prepare("INSERT INTO test VALUES (1);");
    248   Statement *select = db.prepare("SELECT COUNT(*) FROM test");
    249 
    250   auto count = [select] {
    251     int64_t count = -255;
    252     select->exec([&] { count = select->intColumn(0); return false; });
    253     return count;
    254   };
    255 
    256   db.savepoint();
    257 
    258   insert->exec();
    259   REQUIRE(count() == 1);
    260 
    261   SECTION("rollback to savepoint") {
    262     db.restore();
    263     REQUIRE(count() == 0);
    264     try { db.restore(); FAIL("rolled back unexistant savepoint"); }
    265     catch(const reapack_error &) {}
    266   }
    267 
    268   SECTION("release savepoint") {
    269     db.release();
    270     REQUIRE(count() == 1);
    271     try { db.release(); FAIL("released unexistant savepoint"); }
    272     catch(const reapack_error &) {}
    273   }
    274 }