├── .gitignore ├── COPYING ├── Makefile ├── README.md ├── TODO.md └── lua.c /.gitignore: -------------------------------------------------------------------------------- 1 | lua.o 2 | lua.dylib 3 | lua.so 4 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Rob Hoelz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS+=-fPIC 2 | 3 | ifeq ($(shell uname), Darwin) 4 | DYNEXT=dylib 5 | else 6 | DYNEXT=so 7 | endif 8 | 9 | default: lua.$(DYNEXT) 10 | 11 | lua.dylib: lua.o 12 | gcc -o $@ -bundle -undefined dynamic_lookup $^ -llua -lm -ldl 13 | 14 | lua.so: lua.o 15 | gcc -o $@ -shared $^ -llua -lm -ldl 16 | 17 | clean: 18 | rm -f *.o *.$(DYNEXT) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQLite-Lua - Lua extension for SQLite 2 | 3 | This SQLite extension embeds a Lua interpeter into SQLite, so you can execute Lua code in your 4 | SQL statements. 5 | 6 | # Examples 7 | 8 | ## Executing Lua code 9 | 10 | ```sql 11 | select lua('print "hi"'); 12 | ``` 13 | 14 | ## Getting data from Lua 15 | 16 | ```sql 17 | select lua('return math.random(1, 10)'); 18 | ``` 19 | 20 | Since the 'return' is annoying to type and is easy to forget, 21 | the extension automatically prepends a 'return' if it doesn't result in a syntax 22 | error: 23 | 24 | ```sql 25 | select lua('math.random(1, 10)') -- same as above 26 | ``` 27 | 28 | Returning values of a type that doesn't have a sensible equivalent in SQLite (tables, functions, 29 | userdata, and coroutines) causes an error. 30 | 31 | Returned booleans are converted into integers. 32 | 33 | ## Passing data to Lua 34 | 35 | Values passed after the Lua statement are passed into the Lua chunk via a table named arg: 36 | 37 | ```sql 38 | select lua('arg[1] ** 2', value) from values; -- squares values 39 | ``` 40 | 41 | I may introduce a "nicer" syntax for this in the future. 42 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | * Provide bindings for the SQLite API to the Lua interpreter (esp. create\_function) 2 | -------------------------------------------------------------------------------- /lua.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Rob Hoelz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #include "sqlite3ext.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | SQLITE_EXTENSION_INIT1; 30 | 31 | static void 32 | handle_lua_error(lua_State *L, sqlite3_context *ctx) 33 | { 34 | const char *error; 35 | size_t error_len; 36 | 37 | error = lua_tolstring(L, -1, &error_len); 38 | sqlite3_result_error(ctx, error, error_len); 39 | lua_pop(L, 1); 40 | } 41 | 42 | static void 43 | convert_sqlite_values_to_lua(lua_State *L, int nargs, sqlite3_value **values) 44 | { 45 | int i; 46 | 47 | for(i = 0; i < nargs; i++) { 48 | switch(sqlite3_value_type(values[i])) { 49 | case SQLITE_INTEGER: 50 | lua_pushinteger(L, sqlite3_value_int(values[i])); 51 | break; 52 | case SQLITE_FLOAT: 53 | lua_pushnumber(L, sqlite3_value_double(values[i])); 54 | break; 55 | case SQLITE_NULL: 56 | lua_pushnil(L); 57 | break; 58 | case SQLITE_BLOB: 59 | case SQLITE_TEXT: { 60 | size_t length; 61 | 62 | length = sqlite3_value_bytes(values[i]); 63 | lua_pushlstring(L, (const char *) sqlite3_value_text(values[i]), length); 64 | break; 65 | } 66 | } 67 | } 68 | } 69 | 70 | static void 71 | convert_lua_value_to_sqlite(lua_State *L, sqlite3_context *ctx) 72 | { 73 | switch(lua_type(L, -1)) { 74 | case LUA_TSTRING: { 75 | size_t length; 76 | const char *value; 77 | 78 | value = lua_tolstring(L, -1, &length); 79 | 80 | sqlite3_result_text(ctx, value, length, SQLITE_TRANSIENT); 81 | break; 82 | } 83 | case LUA_TNUMBER: 84 | sqlite3_result_double(ctx, lua_tonumber(L, -1)); 85 | break; 86 | case LUA_TBOOLEAN: 87 | sqlite3_result_int(ctx, lua_toboolean(L, -1)); 88 | break; 89 | case LUA_TNIL: 90 | sqlite3_result_null(ctx); 91 | break; 92 | 93 | case LUA_TTABLE: 94 | case LUA_TFUNCTION: 95 | case LUA_TTHREAD: 96 | case LUA_TUSERDATA: { 97 | char *error = NULL; 98 | 99 | error = sqlite3_mprintf("Invalid return type from lua(): %s", lua_typename(L, lua_type(L, -1))); 100 | 101 | sqlite3_result_error(ctx, error, -1); 102 | sqlite3_free(error); 103 | } 104 | } 105 | 106 | lua_pop(L, 1); 107 | } 108 | 109 | static void 110 | insert_args_into_globals(lua_State *L, int nargs) 111 | { 112 | int i; 113 | int stack_offset = lua_gettop(L) - nargs; 114 | 115 | lua_createtable(L, nargs, 0); 116 | for(i = 1; i <= nargs; i++) { 117 | lua_pushinteger(L, i); 118 | lua_pushvalue(L, stack_offset + i); 119 | lua_settable(L, -3); 120 | } 121 | 122 | lua_setglobal(L, "arg"); 123 | } 124 | 125 | static void 126 | sqlite_lua(sqlite3_context *ctx, int nargs, sqlite3_value **args) 127 | { 128 | lua_State *L = (lua_State *) sqlite3_user_data(ctx); 129 | const char *lua_source; 130 | int status; 131 | 132 | if(nargs < 1) { 133 | const char error[] = "No argument passed to lua()"; 134 | sqlite3_result_error(ctx, error, sizeof(error) - 1); 135 | return; 136 | } 137 | 138 | lua_source = (const char *) sqlite3_value_text(args[0]); 139 | 140 | /* we prefix "return " first to enable automatic returning of values */ 141 | lua_pushliteral(L, "return "); 142 | lua_pushstring(L, lua_source); 143 | lua_concat(L, 2); 144 | 145 | status = luaL_loadstring(L, lua_tostring(L, -1)); 146 | lua_remove(L, -2); 147 | 148 | if(status == LUA_ERRSYNTAX) { 149 | lua_pop(L, 1); 150 | status = luaL_loadstring(L, lua_source); 151 | } 152 | 153 | if(status) { 154 | handle_lua_error(L, ctx); 155 | return; 156 | } 157 | 158 | convert_sqlite_values_to_lua(L, nargs - 1, args + 1); 159 | insert_args_into_globals(L, nargs - 1); 160 | 161 | status = lua_pcall(L, nargs - 1, 1, 0); 162 | 163 | if(status) { 164 | handle_lua_error(L, ctx); 165 | return; 166 | } 167 | 168 | convert_lua_value_to_sqlite(L, ctx); 169 | } 170 | 171 | static void * 172 | sqlite_lua_allocator(void *ud, void *ptr, size_t osize, size_t nsize) 173 | { 174 | (void) ud; 175 | (void) osize; 176 | 177 | return sqlite3_realloc(ptr, nsize); 178 | } 179 | 180 | int 181 | sqlite3_extension_init(sqlite3 *db, char **error, const sqlite3_api_routines *api) 182 | { 183 | lua_State *L; 184 | 185 | SQLITE_EXTENSION_INIT2(api); 186 | 187 | L = lua_newstate(sqlite_lua_allocator, NULL); 188 | luaL_openlibs(L); 189 | 190 | if(! L) { 191 | *error = sqlite3_mprintf("Unable to create Lua state"); 192 | return SQLITE_ERROR; 193 | } 194 | 195 | sqlite3_create_function_v2(db, "lua", -1, SQLITE_UTF8, L, sqlite_lua, 196 | NULL, NULL, (void (*)(void*)) lua_close); 197 | 198 | return SQLITE_OK; 199 | } 200 | --------------------------------------------------------------------------------