Skip to content

Alternative flags and encoding support #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,23 @@ int main() {
}
```

Additional flags
----
You can pass additional open flags to SQLite by using a config object:

```c++
sqlite_config config;
config.flags = OpenFlags::READONLY
database db("some_db", config);
int a;
// Now you can only read from db
auto ps = db << "select a from table where something = ? and anotherthing = ?" >> a;
config.flags = OpenFlags::READWRITE | OpenFlags::CREATE; // This is the default
config.encoding = Encoding::UTF16; // The encoding is respected only if you create a new database
database db2("some_db2", config);
// If some_db2 didn't exists before, it will be created with UTF-16 encoding.
```

Prepared Statements
----
It is possible to retain and reuse statments this will keep the query plan and in case of an complex query or many uses might increase the performance significantly.
Expand Down
48 changes: 34 additions & 14 deletions hdr/sqlite_modern_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,41 +405,61 @@ namespace sqlite {
);
}

enum class OpenFlags {
READONLY = SQLITE_OPEN_READONLY,
READWRITE = SQLITE_OPEN_READWRITE,
CREATE = SQLITE_OPEN_CREATE,
NOMUTEX = SQLITE_OPEN_NOMUTEX,
FULLMUTEX = SQLITE_OPEN_FULLMUTEX,
SHAREDCACHE = SQLITE_OPEN_SHAREDCACHE,
PRIVATECACH = SQLITE_OPEN_PRIVATECACHE,
URI = SQLITE_OPEN_URI
};
OpenFlags operator|(const OpenFlags& a, const OpenFlags& b) {
return static_cast<OpenFlags>(static_cast<int>(a) | static_cast<int>(b));
};
enum class Encoding {
ANY = SQLITE_ANY,
UTF8 = SQLITE_UTF8,
UTF16 = SQLITE_UTF16
};
struct sqlite_config {
OpenFlags flags = OpenFlags::READWRITE | OpenFlags::CREATE;
const char *zVfs = nullptr;
Encoding encoding = Encoding::ANY;
};

class database {
protected:
std::shared_ptr<sqlite3> _db;

public:
database(std::u16string const & db_name): _db(nullptr) {
database(const std::string &db_name, const sqlite_config &config = {}): _db(nullptr) {
sqlite3* tmp = nullptr;
auto ret = sqlite3_open16(db_name.data(), &tmp);
auto ret = sqlite3_open_v2(db_name.data(), &tmp, static_cast<int>(config.flags), config.zVfs);
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed.
if(ret != SQLITE_OK) exceptions::throw_sqlite_error(ret);
//_db.reset(tmp, sqlite3_close); // alternative close. (faster?)
if(config.encoding == Encoding::UTF16)
*this << R"(PRAGMA encoding = "UTF-16";)";
}

database(std::string const & db_name): _db(nullptr) {
database(const std::u16string &db_name, const sqlite_config &config = {}): _db(nullptr) {
#ifdef _MSC_VER
auto db_name_utf8 = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>().to_bytes(reinterpret_cast<const wchar_t*>(db_name.c_str()));
#else
auto db_name_utf8 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(db_name);
#endif
sqlite3* tmp = nullptr;
auto ret = sqlite3_open(db_name.data(), &tmp);
auto ret = sqlite3_open_v2(db_name_utf8.data(), &tmp, static_cast<int>(config.flags), config.zVfs);
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed.
if(ret != SQLITE_OK) exceptions::throw_sqlite_error(ret);
//_db.reset(tmp, sqlite3_close); // alternative close. (faster?)
if(config.encoding != Encoding::UTF8)
*this << R"(PRAGMA encoding = "UTF-16";)";
}

database(std::shared_ptr<sqlite3> db):
_db(db) {}

database(const std::string &db_name, const sqlite_config &config): database(db_name) {
(void)config; // Suppress unused warning
}

database(const std::u16string &db_name, const sqlite_config &config): database(db_name) {
(void)config; // Suppress unused warning
}

database_binder operator<<(const std::string& sql) {
return database_binder(_db, sql);
}
Expand Down
119 changes: 119 additions & 0 deletions tests/flags.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sqlite_modern_cpp.h>
#include <endian.h>
using namespace sqlite;
using namespace std;

struct TmpFile
{
string fname;

TmpFile()
{
char f[]="/tmp/sqlite_modern_cpp_test_XXXXXX";
int fid = mkstemp(f);
close(fid);

fname = f;
}

~TmpFile()
{
unlink(fname.c_str());
}
};

#if __BYTE_ORDER == __BIG_ENDIAN
#define OUR_UTF16 "UTF-16be"
#else
#define OUR_UTF16 "UTF-16le"
#endif

int main()
{
try
{
TmpFile file;
sqlite::sqlite_config cfg;
std::string enc;
{
database db(":memory:", cfg);
db << "PRAGMA encoding;" >> enc;
if(enc != "UTF-8") {
cout << "Unexpected encoding on line " << __LINE__ << '\n';
exit(EXIT_FAILURE);
}
}
{
database db(u":memory:", cfg);
db << "PRAGMA encoding;" >> enc;
if(enc != OUR_UTF16) {
cout << "Unexpected encoding on line " << __LINE__ << '\n';
exit(EXIT_FAILURE);
}
}
{
cfg.encoding = Encoding::UTF8;
database db(u":memory:", cfg);
db << "PRAGMA encoding;" >> enc;
if(enc != "UTF-8") {
cout << "Unexpected encoding on line " << __LINE__ << '\n';
exit(EXIT_FAILURE);
}
}
{
cfg.encoding = Encoding::UTF16;
database db(u":memory:", cfg);
db << "PRAGMA encoding;" >> enc;
if(enc != OUR_UTF16) {
cout << "Unexpected encoding on line " << __LINE__ << '\n';
exit(EXIT_FAILURE);
}
}
{
database db(file.fname, cfg);
db << "PRAGMA encoding;" >> enc;
if(enc != OUR_UTF16) {
cout << "Unexpected encoding on line " << __LINE__ << '\n';
exit(EXIT_FAILURE);
}

db << "CREATE TABLE foo (a string);";
db << "INSERT INTO foo VALUES (?)" << "hello";
}
{
cfg.flags = sqlite::OpenFlags::READONLY;
database db(file.fname, cfg);

string str;
db << "SELECT a FROM foo;" >> str;

if(str != "hello")
{
cout << "Bad result on line " << __LINE__ << endl;
exit(EXIT_FAILURE);
}

try {
db << "INSERT INTO foo VALUES (?)" << "invalid";
cout << "Unexpected success on line " << __LINE__ << endl;
exit(EXIT_FAILURE);
} catch(exceptions::readonly&) {}
}
}
catch(sqlite_exception e)
{
cout << "Unexpected error " << e.what() << endl;
exit(EXIT_FAILURE);
}
catch(...)
{
cout << "Unknown error\n";
exit(EXIT_FAILURE);
}

cout << "OK\n";
exit(EXIT_SUCCESS);
}