lmtsqlite.c /size: 6294 b    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6# include "lmtoptional.h"
7
8typedef struct sqlite3_instance sqlite3_instance;
9
10# define SQLITELIB_METATABLE  "luatex.sqlitelib"
11
12typedef struct sqlitelib_data {
13    /*tex There is not much more than a pointer currently. */
14    sqlite3_instance *db;
15} sqlitelib_data ;
16
17typedef struct sqlitelib_state_info {
18
19    int initialized;
20    int padding;
21
22    int (*sqlite3_initialize) (
23        void
24    );
25
26    int (*sqlite3_open) (
27        const char        *filename,
28        sqlite3_instance **ppDb
29    );
30
31    int (*sqlite3_close) (
32        sqlite3_instance *
33    );
34
35    int (*sqlite3_exec) (
36        sqlite3_instance *,
37        const char       *sql,
38        int             (*callback)(void*, int, char**, char**),
39        void             *,
40        char            **errmsg
41    );
42
43    const char * (*sqlite3_errmsg) (
44        sqlite3_instance *
45    );
46
47} sqlitelib_state_info;
48
49static sqlitelib_state_info sqlitelib_state = {
50
51    .initialized    = 0,
52    .padding        = 0,
53
54    .sqlite3_initialize = NULL,
55    .sqlite3_open       = NULL,
56    .sqlite3_close      = NULL,
57    .sqlite3_exec       = NULL,
58    .sqlite3_errmsg     = NULL,
59
60};
61
62static int sqlitelib_initialize(lua_State * L)
63{
64    if (! sqlitelib_state.initialized) {
65        const char *filename = lua_tostring(L, 1);
66        if (filename) {
67
68            lmt_library lib = lmt_library_load(filename);
69
70            sqlitelib_state.sqlite3_initialize = lmt_library_find(lib, "sqlite3_initialize");
71            sqlitelib_state.sqlite3_open       = lmt_library_find(lib, "sqlite3_open");
72            sqlitelib_state.sqlite3_close      = lmt_library_find(lib, "sqlite3_close");
73            sqlitelib_state.sqlite3_exec       = lmt_library_find(lib, "sqlite3_exec");
74            sqlitelib_state.sqlite3_errmsg     = lmt_library_find(lib, "sqlite3_errmsg");
75
76            sqlitelib_state.initialized = lmt_library_okay(lib);
77        }
78        if (sqlitelib_state.initialized) {
79            sqlitelib_state.sqlite3_initialize();
80        }
81    }
82    lua_pushboolean(L, sqlitelib_state.initialized);
83    return 1;
84}
85
86static int sqlitelib_open(lua_State * L)
87{
88    if (sqlitelib_state.initialized) {
89        const char *filename = lua_tostring(L, 1);
90        if (filename != NULL) {
91            sqlitelib_data *data = lua_newuserdatauv(L, sizeof(data), 0);
92            if (! sqlitelib_state.sqlite3_open(filename, &(data->db))) {
93                luaL_getmetatable(L, SQLITELIB_METATABLE);
94                lua_setmetatable(L, -2);
95                return 1;
96            }
97        }
98    }
99    return 0;
100}
101
102static int sqlitelib_close(lua_State * L)
103{
104    if (sqlitelib_state.initialized) {
105        sqlitelib_data * data = luaL_checkudata(L, 1, SQLITELIB_METATABLE);
106        if (data != NULL) {
107            sqlitelib_state.sqlite3_close(data->db);
108            data->db = NULL;
109        }
110    }
111    return 0;
112}
113
114/* we could save the fields in the registry */
115
116static int rows_done = 0; /* can go on stack */
117
118static int sqlitelib_callback(void * L, int nofcolumns, char **values, char **fields)
119{
120    lua_pushvalue(L, -1);
121    lua_pushinteger(L, nofcolumns);
122    if (nofcolumns > 0 && values != NULL) {
123        lua_createtable(L, nofcolumns, 0);
124        for (int i = 0; i < nofcolumns; i++) {
125            lua_pushstring(L, values[i]);
126            lua_rawseti(L, -2, (lua_Integer)i + 1);
127        }
128        if (! rows_done && fields != NULL) {
129            lua_createtable(L, nofcolumns, 0);
130            for (int i = 0; i < nofcolumns; i++) {
131                lua_pushstring(L, fields[i]);
132                lua_rawseti(L, -2, (lua_Integer)i + 1);
133            }
134            lua_call(L, 3, 0);
135        } else {
136            lua_call(L, 2, 0);
137        }
138    } else {
139        lua_call(L, 1, 0);
140    }
141    ++rows_done;
142    return 0;
143}
144
145/* execute(database,querystring,callback) : callback(nofcolumns,values,fields)  */
146
147static int sqlitelib_execute(lua_State * L)
148{
149    if (sqlitelib_state.initialized && ! rows_done) {
150        sqlitelib_data * data = luaL_checkudata(L, 1, SQLITELIB_METATABLE);
151        if (data != NULL) {
152            const char *query = lua_tostring(L, 2);
153            if (query != NULL) {
154                int result = 0;
155                rows_done = 0;
156                if (lua_isfunction(L, 3)) {
157                    result = sqlitelib_state.sqlite3_exec(data->db, query, &sqlitelib_callback, L, NULL);
158                } else {
159                    result = sqlitelib_state.sqlite3_exec(data->db, query, NULL, NULL, NULL);
160                }
161                rows_done = 0;
162                lua_pushboolean(L, ! result);
163                return 1;
164            }
165        }
166    }
167    lua_pushboolean(L, 0);
168    return 1;
169}
170
171static int sqlitelib_getmessage(lua_State * L)
172{
173    if (sqlitelib_state.initialized) {
174        sqlitelib_data * data = luaL_checkudata(L, 1, SQLITELIB_METATABLE);
175        if (data != NULL) {
176            lua_pushstring(L, sqlitelib_state.sqlite3_errmsg(data->db));
177            return 1;
178        }
179    }
180    return 0;
181}
182
183/* private */
184
185static int sqlitelib_free(lua_State * L)
186{
187    return sqlitelib_close(L);
188}
189
190/* <string> = tostring(instance) */
191
192static int sqlitelib_tostring(lua_State * L)
193{
194    if (sqlitelib_state.initialized) {
195        sqlitelib_data * data = luaL_checkudata(L, 1, SQLITELIB_METATABLE);
196        if (data != NULL) {
197            (void) lua_pushfstring(L, "<sqlitelib-instance %p>", data);
198        } else {
199            lua_pushnil(L);
200        }
201        return 1;
202    } else {
203        return 0;
204    }
205}
206
207static const struct luaL_Reg sqlitelib_metatable[] = {
208    { "__tostring", sqlitelib_tostring },
209    { "__gc",       sqlitelib_free     },
210    { NULL,         NULL               },
211};
212
213static struct luaL_Reg sqlitelib_function_list[] = {
214    { "initialize", sqlitelib_initialize },
215    { "open",       sqlitelib_open       },
216    { "close",      sqlitelib_close      },
217    { "execute",    sqlitelib_execute    },
218    { "getmessage", sqlitelib_getmessage },
219    { NULL,         NULL                 },
220};
221
222int luaopen_sqlite(lua_State * L)
223{
224    luaL_newmetatable(L, SQLITELIB_METATABLE);
225    luaL_setfuncs(L, sqlitelib_metatable, 0);
226    lmt_library_register(L, "sqlite", sqlitelib_function_list);
227    return 0;
228}
229