├── .gitignore ├── Makefile ├── README.md ├── TODO ├── config.mk ├── config.mk.openbsd ├── lsqlite.c ├── lsqlite.h ├── lualib.mk ├── luawrap.h └── test.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.so* 2 | /*#* 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include config.mk 2 | include lualib.mk 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | (Yet Another*) Lua wrapper for SQLite wrapper. 2 | 3 | For example usage, see the test suite. Documentation (and more tests) to 4 | come. See also the TODO. 5 | 6 | Basic operation: 7 | 8 | -- Connect to the database 9 | db = sqlite.open() -- defaults to in-memory 10 | 11 | -- exec 12 | assert(db:exec([[ 13 | CREATE TABLE foo ( 14 | id INTEGER PRIMARY KEY, 15 | t TEXT NOT NULL, 16 | s REAL NOT NULL);]])) 17 | 18 | -- compiled statements 19 | local s = db:prepare("INSERT INTO foo (t, s) VALUES (:f, :s);") 20 | 21 | -- binding 22 | s:bind(":f", "foo") 23 | -- OR 24 | s:bind { f="foo", s="bar" } --the : is appended automatically 25 | -- OR 26 | s:bind { "abba zabba", 9999.88888 } -- (for anonymous ? variables) 27 | s:step() 28 | s:reset() 29 | 30 | -- results 31 | local s = db:prepare("SELECT * FROM foo;") 32 | -- multi-result-style 33 | for id, t, score in s:rows("itf") do ... -- "itf" = "as int, text, & float" 34 | -- list of results 35 | for row in s:rows("*l") do ... 36 | -- { column_nameN = column_valueN, ... } table for each row 37 | for row in s:rows("*t") do 38 | 39 | Since Lua doesn't use string encoding, I'm using the UTF-8 version of 40 | SQLite's API throughout. If you need more specific support, Lua's "raw 41 | byte-array and a length"-style strings will work with a Unicode library. 42 | 43 | *Of the two existing, one is moribund (2006) and isn't compatible with 44 | the current version of Lua, and the other has some quirks I don't 45 | care for. 46 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | -*- mode: outline; outline-regexp: " *[*+.-] "; -*- 2 | 3 | * Lua SQLite 3 wrapper 4 | * Documentation 5 | * Tests 6 | + Test busy_handler 7 | + Tests from other SQLite wrappers 8 | * Extension API 9 | + create_function 10 | * Hooks 11 | + TODO commit 12 | + TODO rollback 13 | + TODO progress 14 | + TODO update 15 | * TODO Blobs 16 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | config.mk.openbsd -------------------------------------------------------------------------------- /config.mk.openbsd: -------------------------------------------------------------------------------- 1 | # Project 2 | LIBNAME= sqlite 3 | LIBVER= 1.0 4 | 5 | # Lua-specific paths and libraries 6 | LUA_VER= 5.1 7 | LUA= /usr/local/bin/lua 8 | LUA_LIBPATH= -L/usr/local/lib/ 9 | LUA_LIBS= -llua -lm 10 | LUA_INC= -I/usr/local/include/ 11 | LUA_FLAGS= ${LUA_INC} ${LUA_LIBPATH} ${LUA_LIBS} 12 | 13 | 14 | # Where compiled libraries and .lua sources install. 15 | LUA_DEST_LIB= /usr/local/lib/lua/${LUA_VER}/ 16 | LUA_DEST_LUA= /usr/local/share/lua/${LUA_VER}/ 17 | 18 | 19 | # Additional C settings 20 | CC= cc 21 | LIB_PATHS= -L/usr/local/lib/ 22 | LIBS= -lsqlite3 23 | INC= -I/usr/local/include/ 24 | CFLAGS= -Wall 25 | SHARED= -shared -fPIC 26 | LIBEXT= .so 27 | 28 | 29 | # Other tools, optional 30 | LINT= lint 31 | TESTSUITE= test.lua 32 | ARCHNAME= lua-${LIBNAME} 33 | 34 | 35 | # Build targets 36 | LIBPREFIX= l 37 | LIBFILE= ${LIBNAME}${LIBEXT} 38 | INST_LIB= ${LIBFILE} 39 | -------------------------------------------------------------------------------- /lsqlite.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Scott Vokes 3 | * 4 | * Permission is hereby granted, free of charge, to any person 5 | * obtaining a copy of this software and associated documentation 6 | * files (the "Software"), to deal in the Software without 7 | * restriction, including without limitation the rights to use, 8 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following 11 | * conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include "luawrap.h" 35 | #include "lsqlite.h" 36 | 37 | #define SI static int 38 | #define DEBUG 0 39 | #define TRACE if (DEBUG) printf 40 | 41 | 42 | SI ver(lua_State *L) { lua_pushstring(L, sqlite3_libversion()); return 1; } 43 | SI ver_num(lua_State *L) { lua_pushinteger(L, sqlite3_libversion_number()); return 1; } 44 | 45 | 46 | SI pushres(lua_State *L, int res) { 47 | const char *s; 48 | switch (res) { 49 | #define RS(str) lua_pushstring(L, str); return 1; /* result: success */ 50 | /* Success cases */ 51 | case SQLITE_OK: RS("ok"); /* Successful result */ 52 | case SQLITE_ROW: RS("row"); /* sqlite3_step() has another row ready */ 53 | case SQLITE_DONE: RS("done"); /* sqlite3_step() has finished executing */ 54 | #undef RS 55 | #define RF(str) s = str; break /* result: failure */ 56 | /* Failure cases */ 57 | case SQLITE_ERROR: RF("error"); /* SQL error or missing database */ 58 | case SQLITE_INTERNAL: RF("internal");/* Internal logic error in SQLite */ 59 | case SQLITE_PERM: RF("perm"); /* Access permission denied */ 60 | case SQLITE_ABORT: RF("abort"); /* Callback routine requested an abort */ 61 | case SQLITE_BUSY: RF("busy"); /* The database file is locked */ 62 | case SQLITE_LOCKED: RF("locked"); /* A table in the database is locked */ 63 | case SQLITE_NOMEM: RF("nomem"); /* A malloc() failed */ 64 | case SQLITE_READONLY: RF("readonly");/* Attempt to write a readonly database */ 65 | case SQLITE_INTERRUPT: RF("interrupt"); /* Operation terminated by sqlite3_interrupt()*/ 66 | case SQLITE_IOERR: RF("ioerr"); /* Some kind of disk I/O error occurred */ 67 | case SQLITE_CORRUPT: RF("corrupt"); /* The database disk image is malformed */ 68 | case SQLITE_NOTFOUND: RF("notfound");/* NOT USED. Table or record not found */ 69 | case SQLITE_FULL: RF("full"); /* Insertion failed because database is full */ 70 | case SQLITE_CANTOPEN: RF("cantopen");/* Unable to open the database file */ 71 | case SQLITE_PROTOCOL: RF("protocol");/* NOT USED. Database lock protocol error */ 72 | case SQLITE_EMPTY: RF("empty"); /* Database is empty */ 73 | case SQLITE_SCHEMA: RF("schema"); /* The database schema changed */ 74 | case SQLITE_TOOBIG: RF("toobig"); /* String or BLOB exceeds size limit */ 75 | case SQLITE_CONSTRAINT: RF("constraint"); /* Abort due to constraint violation */ 76 | case SQLITE_MISMATCH: RF("mismatch");/* Data type mismatch */ 77 | case SQLITE_MISUSE: RF("misuse"); /* Library used incorrectly */ 78 | case SQLITE_NOLFS: RF("nolfs"); /* Uses OS features not supported on host */ 79 | case SQLITE_AUTH: RF("auth"); /* Authorization denied */ 80 | case SQLITE_FORMAT: RF("format"); /* Auxiliary database format error */ 81 | case SQLITE_RANGE: RF("range"); /* 2nd parameter to sqlite3_bind out of range */ 82 | case SQLITE_NOTADB: RF("notadb"); /* File opened that is not a database file */ 83 | #undef RF 84 | default: s = "unknown"; break; 85 | } 86 | lua_pushboolean(L, 0); 87 | lua_pushstring(L, s); 88 | return 2; 89 | } 90 | 91 | 92 | /************/ 93 | /* Database */ 94 | /************/ 95 | 96 | static int open_db(lua_State *L, const char *fn) { 97 | sqlite3 *db; 98 | LuaSQLite *ldb; 99 | int res = sqlite3_open(fn, &db); 100 | if (res == SQLITE_OK) { 101 | ldb = (LuaSQLite *)lua_newuserdata(L, sizeof(LuaSQLite)); 102 | ldb->v = db; 103 | luaL_getmetatable(L, "LuaSQLite"); 104 | lua_setmetatable(L, -2); 105 | return 1; 106 | } else { 107 | LERROR(sqlite3_errmsg(db)); 108 | return 0; 109 | } 110 | } 111 | 112 | SI open(lua_State *L) { 113 | size_t len; 114 | const char *fn = luaL_optlstring(L, 1, ":memory:", &len); 115 | return open_db(L, fn); 116 | } 117 | 118 | SI open_memory(lua_State *L) { 119 | return open_db(L, ":memory:"); 120 | } 121 | 122 | SI close(lua_State *L) { 123 | LuaSQLite *ldb = check_db(1); 124 | return pushres(L, sqlite3_close(ldb->v)); 125 | } 126 | 127 | SI interrupt(lua_State *L) { 128 | LuaSQLite *ldb = check_db(1); 129 | sqlite3_interrupt(ldb->v); 130 | return 0; 131 | } 132 | SI db_tostring(lua_State *L) { 133 | LuaSQLite *ldb = check_db(1); 134 | lua_pushfstring(L, "SQLite db: %p", ldb->v); 135 | return 1; 136 | } 137 | 138 | SI db_gc(lua_State *L) { 139 | LuaSQLite *ldb = check_db(1); 140 | sqlite3_close(ldb->v); 141 | return 0; 142 | } 143 | 144 | static void dump(lua_State *L) { 145 | int i; 146 | for (i=1; i<=lua_gettop(L); i++) 147 | printf(" -- %d -> %s (%s)\n", 148 | i, lua_tostring(L, i), lua_typename(L, lua_type(L, i))); 149 | 150 | } 151 | 152 | SI changes(lua_State *L) { 153 | LuaSQLite *ldb = check_db(1); 154 | lua_pushinteger(L, sqlite3_changes(ldb->v)); 155 | return 1; 156 | } 157 | 158 | SI last_insert_rowid(lua_State *L) { 159 | LuaSQLite *ldb = check_db(1); 160 | lua_pushinteger(L, sqlite3_last_insert_rowid(ldb->v)); 161 | return 1; 162 | } 163 | 164 | 165 | /********/ 166 | /* Exec */ 167 | /********/ 168 | 169 | static void push_cell_table(lua_State *L, int len, char** cells) { 170 | int i; 171 | const char *cell; 172 | lua_createtable(L, len, 0); 173 | for (i=0; i < len; i++) { 174 | cell = cells[i]; 175 | if (cell == NULL) 176 | lua_pushnil(L); 177 | else 178 | lua_pushstring(L, cell); 179 | lua_pushinteger(L, i + 1); 180 | lua_insert(L, -2); 181 | lua_settable(L, -3); 182 | } 183 | } 184 | 185 | /* Wrapper to call optional Lua callback. Called once per row. */ 186 | SI exec_cb(void *ls, int ncols, char** colText, char** colNames) { 187 | lua_State *L = (lua_State *) ls; 188 | int ok; 189 | if (DEBUG) printf("in exec_cb, top = %d\n", lua_gettop(L)); 190 | assert(lua_type(L, 3) == LUA_TFUNCTION); 191 | lua_pushvalue(L, 3); /* dup */ 192 | push_cell_table(L, ncols, colNames); 193 | push_cell_table(L, ncols, colText); 194 | ok = lua_pcall(L, 2, 0, 0); 195 | if (DEBUG) printf("ok? %d\n", ok); 196 | return ok != 0; /* 1 -> error, returns abort */ 197 | } 198 | 199 | SI exec(lua_State *L) { 200 | LuaSQLite *ldb = check_db(1); 201 | const char *sql = luaL_checklstring(L, 2, NULL); 202 | char *err; 203 | int top = lua_gettop(L); 204 | int (*cb)(void *,int,char**,char**) = NULL; 205 | int res; 206 | if (top == 3 && lua_type(L, 3) == LUA_TFUNCTION) cb = exec_cb; 207 | 208 | res = sqlite3_exec(ldb->v, sql, cb, (void*)L, &err); 209 | if (res != SQLITE_OK) LERROR(err); 210 | return pushres(L, res); 211 | } 212 | 213 | 214 | /*************/ 215 | /* get_table */ 216 | /*************/ 217 | 218 | SI get_table(lua_State *L) { 219 | LuaSQLite *ldb = check_db(1); 220 | const char *sql = luaL_checkstring(L, 2); 221 | lua_pop(L, 2); 222 | char **qres; 223 | int nrow, ncol; 224 | char *err; 225 | int row, res; 226 | char **cells; 227 | res = sqlite3_get_table(ldb->v, sql, &qres, &nrow, &ncol, &err); 228 | if (res != SQLITE_OK) { 229 | sqlite3_free_table(qres); 230 | lua_pushstring(L, err); 231 | lua_error(L); 232 | } 233 | 234 | /* Make a table of rows, each of which is an array of column strings. */ 235 | lua_createtable(L, nrow, 0); 236 | TRACE("nrow=%d, ncol=%d\n", nrow, ncol); 237 | for (row=0; row <= nrow; row++) { 238 | cells = qres + (ncol * row); 239 | push_cell_table(L, ncol, cells); 240 | 241 | if (row == 0) 242 | lua_pushstring(L, "columns"); 243 | else 244 | lua_pushinteger(L, row); 245 | lua_insert(L, -2); 246 | lua_settable(L, -3); 247 | } 248 | 249 | sqlite3_free_table(qres); 250 | pushres(L, res); 251 | lua_insert(L, -2); 252 | return 2; 253 | } 254 | 255 | 256 | /**********/ 257 | /* Errors */ 258 | /**********/ 259 | 260 | SI errcode(lua_State *L) { 261 | LuaSQLite *ldb = check_db(1); 262 | lua_pushinteger(L, sqlite3_errcode(ldb->v)); 263 | return 1; 264 | } 265 | 266 | SI errmsg(lua_State *L) { 267 | LuaSQLite *ldb = check_db(1); 268 | lua_pushstring(L, sqlite3_errmsg(ldb->v)); 269 | return 1; 270 | } 271 | 272 | 273 | /**************/ 274 | /* Statements */ 275 | /**************/ 276 | 277 | SI prepare(lua_State *L) { 278 | LuaSQLite *ldb = check_db(1); 279 | size_t len; 280 | sqlite3_stmt *stmt; 281 | LuaSQLiteStmt *lstmt; 282 | const char *tail; 283 | const char *sql = luaL_checklstring(L, 2, &len); 284 | int res = sqlite3_prepare_v2(ldb->v, sql, len, &stmt, &tail); 285 | 286 | if (res == SQLITE_OK) { 287 | lstmt = (LuaSQLiteStmt *)lua_newuserdata(L, sizeof(LuaSQLiteStmt *)); 288 | lstmt->v = stmt; 289 | luaL_getmetatable(L, "LuaSQLiteStmt"); 290 | lua_setmetatable(L, -2); 291 | lua_pushstring(L, tail); 292 | return 2; 293 | } else { 294 | LERROR(sqlite3_errmsg(ldb->v)); 295 | return 0; 296 | } 297 | } 298 | 299 | SI stmt_step(lua_State *L) { 300 | LuaSQLiteStmt *s = check_stmt(1); 301 | return pushres(L, sqlite3_step(s->v)); 302 | } 303 | 304 | SI stmt_gc(lua_State *L) { 305 | LuaSQLiteStmt *s = check_stmt(1); 306 | return pushres(L, sqlite3_finalize(s->v)); 307 | } 308 | 309 | SI stmt_reset(lua_State *L) { 310 | LuaSQLiteStmt *s = check_stmt(1); 311 | return pushres(L, sqlite3_reset(s->v)); 312 | } 313 | 314 | SI stmt_tostring(lua_State *L) { 315 | LuaSQLiteStmt *lstmt = check_stmt(1); 316 | lua_pushfstring(L, "SQLite stmt: %p", lstmt->v); 317 | return 1; 318 | } 319 | 320 | SI stmt_sql(lua_State *L) { 321 | LuaSQLiteStmt *lstmt = check_stmt(1); 322 | lua_pushstring(L, sqlite3_sql(lstmt->v)); 323 | return 1; 324 | } 325 | 326 | /***********************/ 327 | /* Columns and binding */ 328 | /***********************/ 329 | 330 | /* int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); */ 331 | /* int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); */ 332 | /* int sqlite3_bind_null(sqlite3_stmt*, int); */ 333 | /* int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); */ 334 | /* int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); */ 335 | /* int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); */ 336 | 337 | SI bind_param_idx(lua_State *L) { 338 | LuaSQLiteStmt *s = check_stmt(1); 339 | const char *name = luaL_checkstring(L, 2); 340 | return sqlite3_bind_parameter_index(s->v, name); 341 | } 342 | 343 | SI param_idx(lua_State *L, sqlite3_stmt *stmt, int i) { 344 | const char* key; 345 | if (lua_isnumber(L, i)) { 346 | return lua_tonumber(L, i); 347 | } else { 348 | key = luaL_checkstring(L, i); 349 | return sqlite3_bind_parameter_index(stmt, key); 350 | } 351 | } 352 | 353 | SI bind_dtype(sqlite3_stmt *s, lua_State *L, int idx) { 354 | int t; 355 | const char *v; 356 | size_t len; 357 | 358 | t = lua_type(L, -1); 359 | TRACE("Binding idx %d to type %d\n", idx, t); 360 | switch (t) { 361 | case LUA_TNIL: 362 | return pushres(L, sqlite3_bind_null(s, idx)); 363 | case LUA_TBOOLEAN: 364 | return pushres(L, sqlite3_bind_int(s, idx, lua_tointeger(L, -1))); 365 | case LUA_TNUMBER: 366 | return pushres(L, sqlite3_bind_double(s, idx, lua_tonumber(L, -1))); 367 | case LUA_TSTRING: 368 | v = luaL_checklstring(L, -1, &len); 369 | return pushres(L, sqlite3_bind_text(s, idx, v, 370 | len, SQLITE_TRANSIENT)); 371 | default: LERROR("Cannot bind type"); 372 | } 373 | return 0; 374 | 375 | } 376 | 377 | /* Take a k=v table and call bind(":" .. k, v) for each. 378 | Take a { v1, v2, v3 } table and call bind([i], v) for each. */ 379 | SI bind_table(lua_State *L, LuaSQLiteStmt *s) { 380 | size_t len = lua_objlen(L, 2); 381 | int i, idx; 382 | const char* vartag = ":"; /* TODO make this an optional argument? */ 383 | 384 | if (len == 0) { /* k=v table */ 385 | lua_pushnil(L); /* start at first key */ 386 | while (lua_next(L, 2) != 0) { 387 | lua_pushlstring(L, vartag, 1); 388 | lua_pushvalue(L, -3); 389 | lua_concat(L, 2); /* prepend : to key */ 390 | idx = sqlite3_bind_parameter_index(s->v, lua_tostring(L, -1)); 391 | if (idx == 0) { 392 | lua_pushfstring(L, "Invalid statement parameter '%s'", 393 | lua_tostring(L, -1)); 394 | lua_error(L); 395 | } 396 | TRACE("Index is %d\n", idx); 397 | lua_pop(L, 1); 398 | bind_dtype(s->v, L, idx); 399 | lua_pop(L, 1); 400 | if (DEBUG) printf("* %s (%s) = %s (%s)\n", 401 | lua_tostring(L, -2), 402 | lua_typename(L, lua_type(L, -2)), 403 | lua_tostring(L, -1), 404 | lua_typename(L, lua_type(L, -1))); 405 | lua_pop(L, 1); 406 | } 407 | } else { /* array */ 408 | for (i=0; i < len; i++) { 409 | lua_pushinteger(L, i + 1); 410 | lua_gettable(L, 2); 411 | bind_dtype(s->v, L, i + 1); 412 | lua_pop(L, 1); 413 | } 414 | } 415 | return 1; 416 | } 417 | 418 | SI stmt_bind(lua_State *L) { 419 | LuaSQLiteStmt *s = check_stmt(1); 420 | int i; 421 | if (lua_type(L, 2) == LUA_TTABLE) return bind_table(L, s); 422 | 423 | i = param_idx(L, s->v, 2); 424 | return bind_dtype(s->v, L, i); 425 | } 426 | 427 | SI bind_param_count(lua_State *L) { 428 | LuaSQLiteStmt *s = check_stmt(1); 429 | return sqlite3_bind_parameter_count(s->v); 430 | } 431 | 432 | SI bind_clear(lua_State *L) { 433 | LuaSQLiteStmt *s = check_stmt(1); 434 | return pushres(L, sqlite3_clear_bindings(s->v)); 435 | } 436 | 437 | SI bind_double(lua_State *L) { 438 | LuaSQLiteStmt *s = check_stmt(1); 439 | int i = param_idx(L, s->v, 2); 440 | double v = luaL_checknumber(L, 3); 441 | return pushres(L, sqlite3_bind_double(s->v, i, v)); 442 | } 443 | 444 | SI bind_int(lua_State *L) { 445 | LuaSQLiteStmt *s = check_stmt(1); 446 | int i = param_idx(L, s->v, 2); 447 | int v = luaL_checkinteger(L, 3); 448 | return pushres(L, sqlite3_bind_int(s->v, i, v)); 449 | } 450 | 451 | SI bind_null(lua_State *L) { 452 | LuaSQLiteStmt *s = check_stmt(1); 453 | int i = param_idx(L, s->v, 2); 454 | return pushres(L, sqlite3_bind_null(s->v, i)); 455 | } 456 | 457 | SI bind_text(lua_State *L) { 458 | LuaSQLiteStmt *s = check_stmt(1); 459 | int i = param_idx(L, s->v, 2); 460 | size_t len; 461 | const char* v = luaL_checklstring(L, 3, &len); 462 | return pushres(L, sqlite3_bind_text(s->v, i, v, len, SQLITE_TRANSIENT)); 463 | } 464 | 465 | 466 | /* const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); */ 467 | /* int sqlite3_column_bytes(sqlite3_stmt*, int iCol); */ 468 | /* int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); */ 469 | /* sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); */ 470 | /* const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); */ 471 | /* sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); */ 472 | 473 | static int sqltype_of_char(lua_State *L, char c) { 474 | switch (c) { 475 | case 'i': return SQLITE_INTEGER; 476 | case 'f': return SQLITE_FLOAT; 477 | case 't': case 's': return SQLITE_TEXT; 478 | case 'b': return SQLITE_BLOB; 479 | case 'n': return SQLITE_NULL; 480 | default: 481 | lua_pushfstring(L, "Invalid column tag '%c' -- must be in 'iftb'", c); 482 | lua_error(L); 483 | } 484 | return -1; 485 | } 486 | 487 | 488 | static void push_col(lua_State *L, sqlite3_stmt *stmt, int i, int t) { 489 | double d; 490 | int n; 491 | const char *c; 492 | size_t tlen; 493 | switch(t) { 494 | case SQLITE_INTEGER: 495 | n = sqlite3_column_int(stmt, i); 496 | lua_pushinteger(L, n); 497 | break; 498 | case SQLITE_FLOAT: 499 | d = sqlite3_column_double(stmt, i); 500 | lua_pushnumber(L, d); 501 | break; 502 | case SQLITE_TEXT: 503 | tlen = sqlite3_column_bytes(stmt, i); 504 | c = sqlite3_column_text(stmt, i); 505 | lua_pushlstring(L, c, tlen); 506 | break; 507 | case SQLITE_BLOB: 508 | tlen = sqlite3_column_bytes(stmt, i); 509 | c = (const char*)sqlite3_column_blob(stmt, i); 510 | lua_pushlstring(L, c, tlen); 511 | break; 512 | case SQLITE_NULL: 513 | lua_pushnil(L); 514 | break; 515 | default: 516 | lua_pushfstring(L, "Bad SQLITE type: %d", t); 517 | lua_error(L); 518 | break; 519 | } 520 | } 521 | 522 | /* Higher level interface, e.g. 523 | * id, key, count, score = s:columns("itif") --int, text, int, float */ 524 | SI columns(lua_State *L) { 525 | LuaSQLiteStmt *s = check_stmt(1); 526 | size_t len; 527 | int i; 528 | const char* cs = luaL_checklstring(L, 2, &len); 529 | 530 | int ct = sqlite3_column_count(s->v); 531 | if (len != ct) { 532 | lua_pushfstring(L, "Invalid column count %d, result has %d columns", len, ct); 533 | lua_error(L); 534 | } 535 | for (i=0; iv, i, sqltype_of_char(L, cs[i])); 536 | 537 | return len; 538 | } 539 | 540 | /* forward references */ 541 | SI row_iter(lua_State *L); 542 | SI row_iter_list(lua_State *L); 543 | SI row_iter_table(lua_State *L); 544 | 545 | struct column { 546 | int type; /* SQLITE_INTEGER, FLOAT, TEXT, BLOB, or NULL */ 547 | const char *name; 548 | }; 549 | 550 | typedef struct col_info { 551 | int ct; 552 | struct column col[0]; 553 | } col_info; 554 | 555 | 556 | static col_info *get_col_info(lua_State *L, sqlite3_stmt *s) { 557 | int ct = sqlite3_column_count(s); 558 | int sz = sizeof(col_info) + ct*(sizeof(struct column)); 559 | int i; 560 | col_info *ci = (col_info *)malloc(sz); 561 | if (ci == NULL) LERROR("col_info malloc fail"); 562 | ci->ct = ct; 563 | for (i=0; icol[i].type = 0; 565 | ci->col[i].name = NULL; 566 | } 567 | return ci; 568 | } 569 | 570 | /* 571 | * High-level statement result interface, returns an iterator. 572 | * First argument: 573 | * "*l" returns an iterator yielding a list of each row's columns 574 | * "*t" returns a col_name=col_val table for each row 575 | * any other string is treated as with columns(s), above. 576 | * 577 | * Optional second argument (TODO): 578 | * If the second argument is a function, it's mapped over each row, 579 | * otherwise the iterator is returned. If the function returns a 580 | * non-true value, the iteration is considered complete and it 581 | * resets the statement. 582 | * 583 | * The iterator/mapper take care of calling s:step(), checking its 584 | * status, and calling s:reset() when complete. 585 | */ 586 | SI rows(lua_State *L) { 587 | LuaSQLiteStmt *s = check_stmt(1); 588 | size_t len; 589 | const char* cols = luaL_checklstring(L, 2, &len); 590 | 591 | /* check for an optional func to map over it */ 592 | /* int hasfunc = (lua_type(L, 3) == LUA_TFUNCTION); */ 593 | col_info *ci; 594 | 595 | if (0) { 596 | dump(L); 597 | printf("(ignore 'unused vars' warning) %p, %s\n", s, cols); 598 | } 599 | if (strcmp(cols, "*l") == 0) { /* value list */ 600 | lua_pop(L, 1); 601 | ci = get_col_info(L, s->v); 602 | lua_pushlightuserdata(L, ci); 603 | lua_pushcclosure(L, row_iter_list, 2); 604 | } else if (strcmp(cols, "*t") == 0) { /* col_name=col_val table */ 605 | lua_pop(L, 1); 606 | ci = get_col_info(L, s->v); 607 | lua_pushlightuserdata(L, ci); 608 | lua_pushcclosure(L, row_iter_table, 2); 609 | } else { 610 | lua_pushcclosure(L, row_iter, 2); 611 | } 612 | return 1; 613 | } 614 | 615 | /* Upvalues: [stmt, cols] */ 616 | SI row_iter(lua_State *L) { 617 | LuaSQLiteStmt *s = check_stmt(lua_upvalueindex(1)); 618 | size_t len; 619 | const char* cols = luaL_checklstring(L, lua_upvalueindex(2), &len); 620 | int i; 621 | 622 | int status = sqlite3_step(s->v); 623 | if (status == SQLITE_ROW) { 624 | for (i=0; iv, i, sqltype_of_char(L, cols[i])); 625 | return len; 626 | } else { 627 | sqlite3_reset(s->v); 628 | if (status != SQLITE_DONE) { pushres(L, status); lua_error(L); } 629 | return 0; 630 | } 631 | } 632 | 633 | static void get_col_types(sqlite3_stmt *stmt, col_info *ci) { 634 | int i; 635 | /* Types are unavailable until after calling s:step(). */ 636 | if (ci->col[0].type == 0) { /* if not already set */ 637 | for (i=0; i < ci->ct; i++) { 638 | ci->col[i].type = sqlite3_column_type(stmt, i); 639 | ci->col[i].name = sqlite3_column_name(stmt, i); 640 | TRACE("col %d -> %d / %s\n", 641 | i, ci->col[i].type, ci->col[i].name); 642 | } 643 | } 644 | 645 | } 646 | 647 | /* Upvalues: [stmt, col_info] */ 648 | SI row_iter_list(lua_State *L) { 649 | LuaSQLiteStmt *s = check_stmt(lua_upvalueindex(1)); 650 | sqlite3_stmt *stmt = s->v; 651 | col_info *ci = (col_info*)lua_touserdata(L, lua_upvalueindex(2)); 652 | int i, status; 653 | assert(ci); 654 | status = sqlite3_step(stmt); 655 | get_col_types(stmt, ci); 656 | if (status == SQLITE_ROW) { 657 | lua_createtable(L, ci->ct, 0); 658 | for (i=0; i < ci->ct; i++) { 659 | lua_pushinteger(L, i+1); 660 | push_col(L, stmt, i, ci->col[i].type); 661 | lua_settable(L, -3); 662 | } 663 | return 1; 664 | } else { 665 | sqlite3_reset(stmt); 666 | free(ci); 667 | if (status != SQLITE_DONE) { pushres(L, status); lua_error(L); } 668 | return 0; 669 | } 670 | } 671 | 672 | SI row_iter_table(lua_State *L) { 673 | LuaSQLiteStmt *s = check_stmt(lua_upvalueindex(1)); 674 | sqlite3_stmt *stmt = s->v; 675 | col_info *ci = (col_info*)lua_touserdata(L, lua_upvalueindex(2)); 676 | int i, status; 677 | assert(ci); 678 | 679 | status = sqlite3_step(stmt); 680 | get_col_types(stmt, ci); 681 | if (status == SQLITE_ROW) { 682 | lua_createtable(L, ci->ct, 0); 683 | for (i=0; i < ci->ct; i++) { 684 | lua_pushstring(L, ci->col[i].name); 685 | push_col(L, stmt, i, ci->col[i].type); 686 | lua_settable(L, -3); 687 | } 688 | return 1; 689 | } else { 690 | sqlite3_reset(stmt); 691 | free(ci); 692 | if (status != SQLITE_DONE) { pushres(L, status); lua_error(L); } 693 | return 0; 694 | } 695 | 696 | return 0; 697 | } 698 | 699 | SI col_double(lua_State *L) { 700 | LuaSQLiteStmt *s = check_stmt(1); 701 | int idx = luaL_checkinteger(L, 2); 702 | double d = sqlite3_column_double(s->v, idx - 1); 703 | lua_pushnumber(L, d); 704 | return 1; 705 | } 706 | 707 | SI col_int(lua_State *L) { 708 | LuaSQLiteStmt *s = check_stmt(1); 709 | int idx = luaL_checkinteger(L, 2); 710 | int i = sqlite3_column_int(s->v, idx - 1); 711 | lua_pushnumber(L, i); 712 | return 1; 713 | } 714 | 715 | SI col_text(lua_State *L) { 716 | LuaSQLiteStmt *s = check_stmt(1); 717 | int idx = luaL_checkinteger(L, 2); 718 | const unsigned char* t = sqlite3_column_text(s->v, idx - 1); 719 | lua_pushstring(L, t); 720 | return 1; 721 | } 722 | 723 | SI col_count(lua_State *L) { 724 | LuaSQLiteStmt *s = check_stmt(1); 725 | lua_pushinteger(L, sqlite3_column_count(s->v)); 726 | return 1; 727 | } 728 | 729 | SI col_typestr(lua_State *L, int type) { 730 | const char *name; 731 | switch (type) { 732 | case SQLITE_INTEGER: name = "integer"; break; 733 | case SQLITE_FLOAT: name = "float"; break; 734 | case SQLITE_TEXT: name = "text"; break; 735 | case SQLITE_BLOB: name = "blob"; break; 736 | case SQLITE_NULL: name = "null"; break; 737 | default: name = "error"; break; 738 | } 739 | lua_pushstring(L, name); 740 | return 1; 741 | } 742 | 743 | SI col_type(lua_State *L) { 744 | LuaSQLiteStmt *s = check_stmt(1); 745 | int idx = luaL_checkinteger(L, 2); 746 | int t = sqlite3_column_type(s->v, idx - 1); 747 | return col_typestr(L, t); 748 | } 749 | 750 | SI col_decltype(lua_State *L) { 751 | LuaSQLiteStmt *s = check_stmt(1); 752 | int idx = luaL_checkinteger(L, 2); 753 | const char *t = sqlite3_column_decltype(s->v, idx - 1); 754 | lua_pushstring(L, t); 755 | return 1; 756 | } 757 | 758 | SI column_database_name(lua_State *L) { 759 | LuaSQLiteStmt *s = check_stmt(1); 760 | int col = luaL_checkinteger(L, 2); 761 | const char *name = sqlite3_column_database_name(s->v, col); 762 | lua_pushstring(L, name); 763 | return 1; 764 | } 765 | 766 | SI column_table_name(lua_State *L) { 767 | LuaSQLiteStmt *s = check_stmt(1); 768 | int col = luaL_checkinteger(L, 2); 769 | const char *name = sqlite3_column_table_name(s->v, col); 770 | lua_pushstring(L, name); 771 | return 1; 772 | } 773 | 774 | SI column_origin_name(lua_State *L) { 775 | LuaSQLiteStmt *s = check_stmt(1); 776 | int col = luaL_checkinteger(L, 2); 777 | const char *name = sqlite3_column_origin_name(s->v, col); 778 | lua_pushstring(L, name); 779 | return 1; 780 | } 781 | 782 | /*********/ 783 | /* Hooks */ 784 | /*********/ 785 | 786 | /* int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); */ 787 | SI busy_cb(void *ud, int callCt) { 788 | lua_State *L = (lua_State *) ud; 789 | int res; 790 | assert(L); 791 | lua_pushlightuserdata(L, L); /* should be per DB, not per VM */ 792 | lua_gettable(L, LUA_REGISTRYINDEX); 793 | if (!lua_isfunction(L, -1)) { 794 | lua_pop(L, 1); 795 | lua_pushstring(L, "No callback function"); 796 | lua_error(L); 797 | return -1; 798 | } 799 | lua_pushinteger(L, callCt); 800 | lua_pcall(L, 1, 1, 0); 801 | res = luaL_checkinteger(L, -1); 802 | return res; 803 | } 804 | 805 | /* static int set_busy_handler */ 806 | SI set_busy_handler(lua_State *L) { 807 | LuaSQLite *db = check_db(1); 808 | if (lua_isfunction(L, 2)) { 809 | lua_pushlightuserdata(L, L); 810 | lua_settable(L, LUA_REGISTRYINDEX); 811 | return pushres(L, sqlite3_busy_handler(db->v, &busy_cb, L)); 812 | } else { 813 | LERROR("set_busy_handler needs function"); 814 | return 0; 815 | } 816 | } 817 | 818 | SI set_busy_timeout(lua_State *L) { 819 | LuaSQLite *db = check_db(1); 820 | int ms = luaL_checkinteger(L, 2); 821 | return pushres(L, sqlite3_busy_timeout(db->v, ms)); 822 | } 823 | 824 | /* void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); */ 825 | /* void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); */ 826 | /* void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); */ 827 | 828 | /* void *sqlite3_update_hook( */ 829 | /* sqlite3*, */ 830 | /* void(*)(void *,int ,char const *,char const *,sqlite3_int64), */ 831 | /* void* */ 832 | /* ); */ 833 | 834 | /*************/ 835 | /* Extension */ 836 | /*************/ 837 | 838 | /* * sqlite3_create_collation() */ 839 | /* * sqlite3_create_module() */ 840 | /* * sqlite3_aggregate_context() */ 841 | /* * sqlite3_result() */ 842 | /* * sqlite3_user_data() */ 843 | /* * sqlite3_value() */ 844 | 845 | 846 | /* int sqlite3_create_function( */ 847 | /* sqlite3 *db, */ 848 | /* const char *zFunctionName, */ 849 | /* int nArg, */ 850 | /* int eTextRep, */ 851 | /* void *pApp, */ 852 | /* void (*xFunc)(sqlite3_context*,int,sqlite3_value**), */ 853 | /* void (*xStep)(sqlite3_context*,int,sqlite3_value**), */ 854 | /* void (*xFinal)(sqlite3_context*) */ 855 | /* ); */ 856 | 857 | 858 | /*********/ 859 | /* Blobs */ 860 | /*********/ 861 | 862 | 863 | /********/ 864 | /* Misc */ 865 | /********/ 866 | 867 | /* sqlite3_int64 sqlite3_memory_used(void); */ 868 | /* sqlite3_int64 sqlite3_memory_highwater(int resetFlag); */ 869 | 870 | SI sleep(lua_State *L) { 871 | int ms = luaL_checkinteger(L, 1); 872 | return pushres(L, sqlite3_sleep(ms)); 873 | } 874 | 875 | 876 | /*************************/ 877 | /* Module and metatables */ 878 | /*************************/ 879 | 880 | /* General database connection. */ 881 | LIB(db_mt) = { 882 | { "exec", exec }, 883 | { "get_table", get_table }, 884 | { "errcode", errcode }, 885 | { "errmsg", errmsg }, 886 | { "prepare", prepare }, 887 | { "close", close }, 888 | { "interrupt", interrupt }, 889 | { "changes", changes }, 890 | { "set_busy_handler", set_busy_handler }, 891 | { "set_busy_timeout", set_busy_timeout }, 892 | { "last_insert_rowid", last_insert_rowid }, 893 | { "__tostring", db_tostring }, 894 | { "__gc", db_gc }, 895 | { NULL, NULL }, 896 | }; 897 | 898 | LIB(stmt_mt) = { 899 | { "step", stmt_step }, 900 | { "reset", stmt_reset }, 901 | { "bind", stmt_bind }, 902 | { "bind_double", bind_double }, 903 | { "bind_int", bind_int }, 904 | { "bind_null", bind_null }, 905 | { "bind_text", bind_text }, 906 | { "bind_param_count", bind_param_count }, 907 | { "bind_param_index", bind_param_idx }, 908 | { "clear", bind_clear }, 909 | { "column_double", col_double }, 910 | { "column_int", col_int }, 911 | { "column_text", col_text }, 912 | { "column_type", col_type }, 913 | { "column_decltype", col_decltype }, 914 | { "column_count", col_count }, 915 | { "columns", columns }, 916 | { "rows", rows }, 917 | { "sql", stmt_sql }, 918 | { "database_name", column_database_name }, 919 | { "table_name", column_table_name }, 920 | { "origin_name", column_origin_name }, 921 | { "__tostring", stmt_tostring }, 922 | { "__gc", stmt_gc }, 923 | { NULL, NULL }, 924 | }; 925 | 926 | /* Binary blob handle. */ 927 | /* ... */ 928 | 929 | 930 | LIB(sqlite_lib) = { 931 | { "open", open }, 932 | { "open_memory", open_memory }, 933 | { "version", ver }, 934 | { "version_number", ver_num }, 935 | { "sleep", sleep }, 936 | { NULL, NULL }, 937 | }; 938 | 939 | int luaopen_sqlite(lua_State *L) { 940 | set_MT("LuaSQLite", db_mt); 941 | set_MT("LuaSQLiteStmt", stmt_mt); 942 | luaL_register(L, "sqlite", sqlite_lib); 943 | return 1; 944 | } 945 | -------------------------------------------------------------------------------- /lsqlite.h: -------------------------------------------------------------------------------- 1 | #ifndef LSQLITE_H 2 | #define LSQLITE_H 3 | 4 | BOX_UD(sqlite3, LuaSQLite); 5 | BOX_UD(sqlite3_stmt, LuaSQLiteStmt); 6 | 7 | #define check_db(n) CHECK_UD(LuaSQLite, n) 8 | #define check_stmt(n) CHECK_UD(LuaSQLiteStmt, n) 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /lualib.mk: -------------------------------------------------------------------------------- 1 | # Makefile for Lua libraries written in C. 2 | 3 | all: ${LIBFILE} 4 | 5 | clean: 6 | rm -f ${LIBNAME}${LIBEXT}* ${ARCHNAME}*.tar.gz ${ARCHNAME}*.zip *.core 7 | 8 | ${LIBFILE}: ${LIBPREFIX}${LIBNAME}.c 9 | ${CC} -o $@ $> ${CFLAGS} ${SHARED} ${LUA_FLAGS} \ 10 | ${INC} ${LIB_PATHS} ${LIBS} 11 | 12 | test: ${LIBFILE} 13 | ${LUA} ${TESTSUITE} 14 | 15 | lint: ${LIBPREFIX}${LIBNAME}.c 16 | ${LINT} ${INC} ${LUA_INC} $> 17 | 18 | tar: 19 | git archive --format=tar --prefix=${ARCHNAME}-${LIBVER}/ HEAD^{tree} \ 20 | | gzip > ${ARCHNAME}-${LIBVER}.tar.gz 21 | 22 | zip: 23 | git archive --format=zip --prefix=${ARCHNAME}-${LIBVER}/ HEAD^{tree} \ 24 | > ${ARCHNAME}-${LIBVER}.zip 25 | 26 | 27 | gdb: 28 | gdb `which lua` lua.core 29 | 30 | install: ${LIBFILE} 31 | cp ${INST_LIB} ${LUA_DEST_LIB} 32 | cd ${LUA_DEST_LIB} && ln -s ${INST_LIB} ${LIBNAME}${LIBEXT} 33 | 34 | uninstall: 35 | rm -f ${LUA_DEST_LIB}${LIBNAME}${LIBEXT}* 36 | -------------------------------------------------------------------------------- /luawrap.h: -------------------------------------------------------------------------------- 1 | #ifndef LUAWRAP_H 2 | #define LUAWRAP_H 3 | 4 | /* Utility macros for wrapping C libraries */ 5 | 6 | #define LIB(name) static const struct luaL_Reg name[] 7 | #define SI static int 8 | 9 | #define LERROR(s) \ 10 | { lua_pushstring(L, s); lua_error(L); } 11 | 12 | #define CHECK_UD(mt, n) ((mt *)luaL_checkudata(L, n, #mt)) 13 | 14 | #define NEW_UD(mt) (mt *)lua_newuserdata(L, sizeof(mt *)) 15 | 16 | /* Box a C struct as a full userdata. */ 17 | #define BOX_UD(cstruct, lua_ud) \ 18 | typedef struct lua_ud { \ 19 | cstruct *v; \ 20 | } lua_ud \ 21 | 22 | /* Associate a metatable with its name. Use in luaopen_[libname]. */ 23 | #define set_MT(mt, t) \ 24 | luaL_newmetatable(L, mt); \ 25 | lua_pushvalue(L, -1); \ 26 | lua_setfield(L, -2, "__index"); \ 27 | luaL_register(L, NULL, t); \ 28 | lua_pop(L, 1) 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | require "lunatest" 2 | require "sqlite" 3 | 4 | print("SQLite Version: ", sqlite.version(), sqlite.version_number()) 5 | 6 | 7 | function setup(name) 8 | db = sqlite.open() 9 | assert(db:exec([[ 10 | CREATE TABLE foo ( 11 | id INTEGER PRIMARY KEY, 12 | t TEXT NOT NULL, 13 | s REAL NOT NULL);]])) 14 | end 15 | 16 | local function step_and_reset(s) 17 | assert_equal("done", s:step()) 18 | assert_equal("ok", s:reset()) 19 | end 20 | 21 | 22 | ------------- 23 | -- Binding -- 24 | ------------- 25 | 26 | -- Bind by s:bind(":key", value) 27 | function test_bind_imperative() 28 | local s = db:prepare("INSERT INTO foo (t, s) VALUES (:f, :s);") 29 | s:bind(":f", "foo") 30 | s:bind(":s", 1234.5) 31 | step_and_reset(s) 32 | end 33 | 34 | -- Bind by array of values 35 | function test_bind_array() 36 | local s = db:prepare("INSERT INTO foo (t, s) VALUES (?, ?);") 37 | s:bind { "abba zabba", 9999.88888 } 38 | step_and_reset(s) 39 | end 40 | 41 | -- Bind by k=v table 42 | function test_bind_table() 43 | local s = db:prepare("INSERT INTO foo (t, s) VALUES (:f, :s);") 44 | s:bind { f="banana", s=30949 } 45 | step_and_reset(s) 46 | end 47 | 48 | 49 | ------------------ 50 | -- Getting rows -- 51 | ------------------ 52 | 53 | local function add_sample_data_1() 54 | local s = db:prepare("INSERT INTO foo (t, s) VALUES (:f, :s);") 55 | for _,row in ipairs{{"foo", 123}, 56 | {"bar", 456}, 57 | {"baz", 789}} do 58 | s:bind { f=row[1], s=row[2] } 59 | step_and_reset(s) 60 | end 61 | end 62 | 63 | function test_select() 64 | add_sample_data_1() 65 | local s = db:prepare("SELECT * FROM foo;") 66 | while s:step() == "row" do 67 | local id, t, f = s:columns("itf") 68 | assert_number(id) 69 | assert_string(t) 70 | assert_number(f) 71 | end 72 | assert_equal("ok", s:reset()) 73 | end 74 | 75 | function test_row_iter_multi() 76 | add_sample_data_1() 77 | local s = db:prepare("SELECT * FROM foo;") 78 | for id, t, score in s:rows("itf") do 79 | assert_number(id); assert_string(t); assert_number(score) 80 | end 81 | end 82 | 83 | function test_row_iter_list() 84 | add_sample_data_1() 85 | local s = db:prepare("SELECT * FROM foo;") 86 | for row in s:rows("*l") do 87 | assert_number(row[1]); assert_string(row[2]); assert_number(row[3]) 88 | end 89 | end 90 | 91 | function test_row_iter_table() 92 | add_sample_data_1() 93 | local s = db:prepare("SELECT * FROM foo;") 94 | for row in s:rows("*t") do 95 | assert_number(row.id); assert_string(row.t); assert_number(row.s) 96 | end 97 | end 98 | 99 | 100 | --------------- 101 | -- Get_table -- 102 | --------------- 103 | 104 | function test_get_table() 105 | add_sample_data_1() 106 | local status, res = db:get_table("SELECT * FROM foo;") 107 | assert_equal("ok", status) 108 | assert_equal(3, #res) 109 | -- (sqlite3_get_table returns every value as a string) 110 | assert_equal("1", res[1][1]) 111 | assert_equal("bar", res[2][2]) 112 | assert_equal("baz", res[3][2]) 113 | assert_equal("789.0", res[3][3]) 114 | end 115 | 116 | 117 | ---------------------- 118 | -- exec() and hooks -- 119 | ---------------------- 120 | 121 | function test_exec1() 122 | add_sample_data_1() 123 | local first = true 124 | local hook = function(colnames, cols) 125 | if first then 126 | local head = table.concat(colnames, " | ") 127 | print("\n" .. head) 128 | print(("-"):rep(head:len())) 129 | first = false 130 | end 131 | print(table.concat(cols, " | ")) 132 | end 133 | assert_equal("ok", db:exec("SELECT * FROM foo;", hook)) 134 | assert_false(first) 135 | end 136 | 137 | function test_exec_error() 138 | add_sample_data_1() 139 | local first = true 140 | local hook = function(colnames, cols) 141 | error("bananas") 142 | end 143 | local ok, err = pcall(function() db:exec("SELECT * FROM foo;", hook) end) 144 | assert_false(ok, "should catch the error") 145 | assert_equal("callback requested query abort", err) 146 | end 147 | 148 | 149 | -------------------- 150 | -- Error handling -- 151 | -------------------- 152 | 153 | function test_error_constraint() 154 | local s = db:prepare("INSERT INTO foo (t, s) VALUES (:f, :s);") 155 | s:bind { f="foo", s=nil } 156 | local status, err = s:step() 157 | assert_false(status) 158 | assert_equal("constraint", err) 159 | end 160 | 161 | 162 | ----------------- 163 | -- Busy status -- 164 | ----------------- 165 | 166 | function test_busy_timeout() 167 | assert_equal("ok", db:set_busy_timeout(30)) 168 | end 169 | 170 | function test_busy_handler() 171 | -- TODO: make SQLite call the callback 172 | local status, err = db:set_busy_handler(function(ct) error("AAAAH") end) 173 | assert_equal("ok", status) 174 | end 175 | 176 | 177 | lunatest.run() 178 | --------------------------------------------------------------------------------