Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ Debug
Release
TestResults

sqlitelib_wrap.cxx
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
122 changes: 37 additions & 85 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 <sqlitelib.h>
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<char>({ 'A', 'B', 'C', 'D' }));
stmt.execute("paul", 20, vector<char>({ 'E', 'B', 'G', 'H' }));
stmt.execute("mark", 15, vector<char>({ 'I', 'J', 'K', 'L' }));
stmt.execute("luke", 25, vector<char>({ 'M', 'N', 'O', 'P' }));

## Select a record (single colum)

auto val = db.execute_value<int>("SELECT age FROM people WHERE name='john'");
val; // 10

## Select records (multiple columns)

auto rows = db.execute<int, std::string>("SELECT age, name FROM people");
rows.size(); // 4

auto [age, name] = rows[3]; // age: 25, name: luke

## Bind #1

auto stmt = db.prepare<std::string>("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<int>("SELECT id FROM people WHERE name=? AND age=?", "john", 10);
val; // 1

## Cursor (multiple columns)

auto stmt = db.prepare<std::string, int>("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<std::string>("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<int>("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<string, int>("SELECT name, age FROM people")) {
;
}
## Check connection

for (const auto& x: db.execute_cursor<std::string>("SELECT name FROM people")) {
;
}
```lua
print(db:is_open()) -- true
```

License
-------

MIT license (© 2021 Yuji Hirose)
MIT license (© 2021 Yuji Hirose, © 2026 rycamosun)
6 changes: 3 additions & 3 deletions sqlitelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}

Expand Down Expand Up @@ -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;
}
Expand Down
64 changes: 64 additions & 0 deletions sqlitelib.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
%module sqlitelib

%{
#include "sqlitelib.h"
#include <sqlite3.h>
using namespace sqlitelib;
template<typename Tag, typename Tag::type M>
struct Rob { friend typename Tag::type get(Tag) { return M; } };
struct SqliteDb { typedef sqlite3* sqlitelib::Sqlite::*type; friend type get(SqliteDb); };
template struct Rob<SqliteDb, &sqlitelib::Sqlite::db_>;
%}

%include <std_string.i>
%include <std_vector.i>
%include <exception.i>

#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<std::string>;

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<std::string>& 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<std::string> execute_string(const char* q) {
return self->execute<std::string>(q);
}
std::vector<std::string> execute_string(const char* q, const std::vector<std::string>& 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<std::string> out;
while (sqlite3_step(s) == SQLITE_ROW)
out.emplace_back(reinterpret_cast<const char*>(sqlite3_column_text(s, 0)),
sqlite3_column_bytes(s, 0));
sqlite3_finalize(s);
return out;
}
}
10 changes: 10 additions & 0 deletions test/Makefile
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions test/example.lua
Original file line number Diff line number Diff line change
@@ -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