diff --git a/.gitignore b/.gitignore index c7e568f..d15bbba 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ Debug Release TestResults +sqlitelib_wrap.cxx diff --git a/LICENSE b/LICENSE index 2fb2c65..034b51a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2020 yhirose +Copyright (c) 2026 rycamosun Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f2846c8..8ca4cf7 100644 --- a/README.md +++ b/README.md @@ -1,107 +1,59 @@ -cpp-sqlitelib +lua-sqlitelib ============= -A single file C++ header-only SQLite wrapper library +A Lua SQLite library via SWIG bindings over [cpp-sqlitelib](https://github.com/yhirose/cpp-sqlitelib). ## Open database - #include - using namespace sqlitelib; - auto db = Sqlite("./test.db"); +```lua +local sqlitelib = require("sqlitelib") + +local db = sqlitelib.Sqlite(":memory:") +-- or a file: +local db = sqlitelib.Sqlite("./test.db") +``` ## Create table - db.execute(R"( - CREATE TABLE IF NOT EXISTS people ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT, - age INTEGER, - data BLOB - ) - )"); +```lua +db:execute([[ + CREATE TABLE IF NOT EXISTS people ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, + age INTEGER + ) +]]) +``` ## Drop table - db.execute("DROP TABLE IF EXISTS people"); +```lua +db:execute("DROP TABLE IF EXISTS people") +``` -## Insert records - - auto stmt = db.prepare("INSERT INTO people (name, age, data) VALUES (?, ?, ?)"); - stmt.execute("john", 10, vector({ 'A', 'B', 'C', 'D' })); - stmt.execute("paul", 20, vector({ 'E', 'B', 'G', 'H' })); - stmt.execute("mark", 15, vector({ 'I', 'J', 'K', 'L' })); - stmt.execute("luke", 25, vector({ 'M', 'N', 'O', 'P' })); - -## Select a record (single colum) - - auto val = db.execute_value("SELECT age FROM people WHERE name='john'"); - val; // 10 - -## Select records (multiple columns) - - auto rows = db.execute("SELECT age, name FROM people"); - rows.size(); // 4 - - auto [age, name] = rows[3]; // age: 25, name: luke - -## Bind #1 - - auto stmt = db.prepare("SELECT name FROM people WHERE age > ?"); - - auto rows = stmt.execute(10); - rows.size(); // 3 - rows[0]; // paul - auto rows = stmt.bind(10).execute(); - rows.size(); // 3 - rows[0]; // paul - -## Bind #2 - - auto val = db.execute_value("SELECT id FROM people WHERE name=? AND age=?", "john", 10); - val; // 1 - -## Cursor (multiple columns) - - auto stmt = db.prepare("SELECT name, age FROM people"); - - auto cursor = stmt.execute_cursor(); - for (auto it = cursor.begin(); it != cursor.end(); ++it) { - get<0>(*it); - get<1>(*it); - ++it; - } - - // With C++17 structured binding - for (const auto& [name, age] : stmt.execute_cursor()) { - ; - } - -## Cursor (single column) - - auto stmt = db.prepare("SELECT name FROM people"); - - for (const auto& x: stmt.execute_cursor()) { - ; - } +## Insert records -## Count +```lua +db:execute("INSERT INTO people (name, age) VALUES ('fish', 10)") +``` - auto val = db.execute_value("SELECT COUNT(*) FROM people"); - val; // 4 +## Select a records -## Flat API +```lua +local names = db:execute_string("SELECT name FROM people") +for i = 0, names:size() - 1 do + print(names[i]) +end +``` - for (const auto& [name, age] : - db.execute_cursor("SELECT name, age FROM people")) { - ; - } +## Check connection - for (const auto& x: db.execute_cursor("SELECT name FROM people")) { - ; - } +```lua +print(db:is_open()) -- true +``` License ------- -MIT license (© 2021 Yuji Hirose) +MIT license (© 2021 Yuji Hirose, © 2026 rycamosun) diff --git a/sqlitelib.h b/sqlitelib.h index 5905292..6cf18d5 100644 --- a/sqlitelib.h +++ b/sqlitelib.h @@ -24,7 +24,7 @@ namespace detail { inline void verify(int rc, int expected = SQLITE_OK) { if (rc != expected) { - throw std::exception(); + throw std::runtime_error(sqlite3_errstr(rc)); } } @@ -159,10 +159,10 @@ class Iterator { } else if (rc == SQLITE_DONE) { id_ = -1; } else { - throw std::exception(); // TODO: + throw std::runtime_error(sqlite3_errmsg(sqlite3_db_handle(stmt_))); // NOTE: Placeholders } } else { - throw std::exception(); // TODO: + throw std::logic_error("operator++ called on invalid iterator"); // NOTE: Placeholders } return *this; } diff --git a/sqlitelib.i b/sqlitelib.i new file mode 100644 index 0000000..b7c43be --- /dev/null +++ b/sqlitelib.i @@ -0,0 +1,64 @@ +%module sqlitelib + +%{ +#include "sqlitelib.h" +#include +using namespace sqlitelib; +template +struct Rob { friend typename Tag::type get(Tag) { return M; } }; +struct SqliteDb { typedef sqlite3* sqlitelib::Sqlite::*type; friend type get(SqliteDb); }; +template struct Rob; +%} + +%include +%include +%include + +#pragma SWIG nowarn=302,509 + +%exception { + try { $action } + catch (const std::exception& e) { + luaL_error(L, "%s", e.what()); return SWIG_ERROR; + } +} + +%template(VectorString) std::vector; + +namespace sqlitelib { + +class Sqlite { +public: + Sqlite(const char* path); + ~Sqlite(); + bool is_open() const; +}; + +} + +%extend sqlitelib::Sqlite { + void execute(const char* q) { self->execute(q); } + void execute(const char* q, const std::vector& p) { + sqlite3_stmt* s; + sqlite3_prepare_v2(self->*get(SqliteDb()), q, -1, &s, nullptr); + for (int i = 0; i < (int)p.size(); i++) + sqlite3_bind_text(s, i+1, p[i].data(), p[i].size(), SQLITE_TRANSIENT); + sqlite3_step(s); + sqlite3_finalize(s); + } + std::vector execute_string(const char* q) { + return self->execute(q); + } + std::vector execute_string(const char* q, const std::vector& p) { + sqlite3_stmt* s; + sqlite3_prepare_v2(self->*get(SqliteDb()), q, -1, &s, nullptr); + for (int i = 0; i < (int)p.size(); i++) + sqlite3_bind_text(s, i+1, p[i].data(), p[i].size(), SQLITE_TRANSIENT); + std::vector out; + while (sqlite3_step(s) == SQLITE_ROW) + out.emplace_back(reinterpret_cast(sqlite3_column_text(s, 0)), + sqlite3_column_bytes(s, 0)); + sqlite3_finalize(s); + return out; + } +} diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..955b312 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,10 @@ +all: + swig -c++ -lua -I.. ../sqlitelib.i + g++ -std=c++14 -fPIC -shared \ + $(shell pkg-config --cflags --libs lua sqlite3) \ + -I.. ../sqlitelib_wrap.cxx -o sqlitelib.so + +clean: + rm -f sqlitelib_wrap.cxx sqlitelib.so + +.PHONY: all clean diff --git a/test/example.lua b/test/example.lua new file mode 100644 index 0000000..ee8328e --- /dev/null +++ b/test/example.lua @@ -0,0 +1,15 @@ +local sqlitelib = require("sqlitelib") + +local db = sqlitelib.Sqlite(":memory:") + +db:execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, score REAL)") +db:execute("INSERT INTO users VALUES (1, 'Ry', 9.5)") + +local e = sqlitelib.VectorString() +e:push_back("Eh") +db:execute("INSERT INTO users VALUES (2, ?, 7.0)", e) + +local names = db:execute_string("SELECT name FROM users") +for i = 0, names:size() - 1 do + print(names[i]) +end