lmtmysql.c /size: 10 Kb    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 void           mysql_instance;
9typedef void           mysql_result;
10typedef char         **mysql_row;
11typedef unsigned int   mysql_offset;
12
13typedef struct mysql_field {
14    char          *name;
15    char          *org_name;
16    char          *table;
17    char          *org_table;
18    char          *db;
19    char          *catalog;
20    char          *def;
21    unsigned long  length;
22    unsigned long  max_length;
23    unsigned int   name_length;
24    unsigned int   org_name_length;
25    unsigned int   table_length;
26    unsigned int   org_table_length;
27    unsigned int   db_length;
28    unsigned int   catalog_length;
29    unsigned int   def_length;
30    unsigned int   flags;
31    unsigned int   decimals;
32    unsigned int   charsetnr;
33    int            type;
34    void          *extension;
35} mysql_field;
36
37# define MYSQLLIB_METATABLE  "luatex.mysqllib"
38
39typedef struct mysqllib_data {
40    /*tex There is not much more than a pointer currently. */
41    mysql_instance * db;
42} mysqllib_data ;
43
44typedef struct mysqllib_state_info {
45
46    int initialized;
47    int padding;
48
49    mysql_instance * (*mysql_init) (
50        mysql_instance *mysql
51    );
52
53    mysql_instance * (*mysql_real_connect) (
54        mysql_instance *mysql,
55        const char     *host,
56        const char     *user,
57        const char     *passwd,
58        const char     *db,
59        unsigned int    port,
60        const char     *unix_socket,
61        unsigned long   clientflag
62    );
63
64    unsigned int (*mysql_errno) (
65        mysql_instance *mysql
66    );
67
68    const char * (*mysql_error) (
69        mysql_instance *mysql
70    );
71
72    int (*mysql_real_query) (
73        mysql_instance *mysql,
74        const char     *q,
75        unsigned long   length
76    );
77
78    mysql_result * (*mysql_store_result) (
79        mysql_instance *mysql
80    );
81
82    void (*mysql_free_result) (
83        mysql_result *result
84    );
85
86    unsigned long long (*mysql_num_rows) (
87        mysql_result *res
88    );
89
90    mysql_row (*mysql_fetch_row) (
91        mysql_result *result
92    );
93
94    unsigned int (*mysql_affected_rows) (
95        mysql_instance *mysql
96    );
97
98    unsigned int (*mysql_field_count) (
99        mysql_instance *mysql
100    );
101
102    unsigned int (*mysql_num_fields) (
103        mysql_result *res
104    );
105
106    mysql_field * (*mysql_fetch_fields) (
107        mysql_result *res
108    );
109
110    mysql_offset (*mysql_field_seek) (
111        mysql_result *result,
112        mysql_offset  offset
113    );
114
115    void (*mysql_close) (
116        mysql_instance *sock
117    );
118
119} mysqllib_state_info;
120
121static mysqllib_state_info mysqllib_state = {
122
123    .initialized         = 0,
124    .padding             = 0,
125
126    .mysql_init          = NULL,
127    .mysql_real_connect  = NULL,
128    .mysql_errno         = NULL,
129    .mysql_error         = NULL,
130    .mysql_real_query    = NULL,
131    .mysql_store_result  = NULL,
132    .mysql_free_result   = NULL,
133    .mysql_num_rows      = NULL,
134    .mysql_fetch_row     = NULL,
135    .mysql_affected_rows = NULL,
136    .mysql_field_count   = NULL,
137    .mysql_num_fields    = NULL,
138    .mysql_fetch_fields  = NULL,
139    .mysql_field_seek    = NULL,
140    .mysql_close         = NULL,
141
142};
143
144static int mysqllib_initialize(lua_State * L)
145{
146    if (! mysqllib_state.initialized) {
147        const char *filename = lua_tostring(L, 1);
148        if (filename != NULL) {
149
150            lmt_library lib = lmt_library_load(filename);
151
152            mysqllib_state.mysql_init          = lmt_library_find(lib, "mysql_init" );
153            mysqllib_state.mysql_real_connect  = lmt_library_find(lib, "mysql_real_connect" );
154            mysqllib_state.mysql_errno         = lmt_library_find(lib, "mysql_errno" );
155            mysqllib_state.mysql_error         = lmt_library_find(lib, "mysql_error" );
156            mysqllib_state.mysql_real_query    = lmt_library_find(lib, "mysql_real_query" );
157            mysqllib_state.mysql_store_result  = lmt_library_find(lib, "mysql_store_result" );
158            mysqllib_state.mysql_free_result   = lmt_library_find(lib, "mysql_free_result" );
159            mysqllib_state.mysql_num_rows      = lmt_library_find(lib, "mysql_num_rows" );
160            mysqllib_state.mysql_fetch_row     = lmt_library_find(lib, "mysql_fetch_row" );
161            mysqllib_state.mysql_affected_rows = lmt_library_find(lib, "mysql_affected_rows" );
162            mysqllib_state.mysql_field_count   = lmt_library_find(lib, "mysql_field_count" );
163            mysqllib_state.mysql_num_fields    = lmt_library_find(lib, "mysql_num_fields" );
164            mysqllib_state.mysql_fetch_fields  = lmt_library_find(lib, "mysql_fetch_fields" );
165            mysqllib_state.mysql_field_seek    = lmt_library_find(lib, "mysql_field_seek" );
166            mysqllib_state.mysql_close         = lmt_library_find(lib, "mysql_close" );
167
168            mysqllib_state.initialized = lmt_library_okay(lib);
169        }
170    }
171    lua_pushboolean(L, mysqllib_state.initialized);
172    return 1;
173}
174
175static int mysqllib_open(lua_State * L)
176{
177    if (mysqllib_state.initialized) {
178        const char     * database   = luaL_checkstring(L, 1);
179        const char     * username   = luaL_optstring(L, 2, NULL);
180        const char     * password   = luaL_optstring(L, 3, NULL);
181        const char     * host       = luaL_optstring(L, 4, NULL);
182        int              port       = lmt_optinteger(L, 5, 0);
183        const char     * socket     = NULL; /* luaL_optstring(L, 6, NULL); */
184        int              flag       = 0;    /* luaL_optinteger(L, 7, 0); */
185        mysql_instance * db         = mysqllib_state.mysql_init(NULL);
186        if (db != NULL) {
187            if (mysqllib_state.mysql_real_connect(db, host, username, password, database, port, socket, flag)) {
188                mysqllib_data *data = lua_newuserdatauv(L, sizeof(data), 0);
189                data->db = db ;
190                luaL_getmetatable(L, MYSQLLIB_METATABLE);
191                lua_setmetatable(L, -2);
192                return 1;
193            } else {
194                mysqllib_state.mysql_close(db);
195            }
196        }
197    }
198    return 0;
199}
200
201static int mysqllib_close(lua_State * L)
202{
203    if (mysqllib_state.initialized) {
204        mysqllib_data * data = luaL_checkudata(L, 1, MYSQLLIB_METATABLE);
205        if (data != NULL) {
206            mysqllib_state.mysql_close(data->db);
207            data->db = NULL;
208        }
209    }
210    return 0;
211}
212
213/* execute(database,querystring,callback) : callback(nofcolumns,values,fields)  */
214
215static int mysqllib_execute(lua_State * L)
216{
217    if (mysqllib_state.initialized) {
218        mysqllib_data * data = luaL_checkudata(L, 1, MYSQLLIB_METATABLE);
219        if (data != NULL) {
220            size_t length = 0;
221            const char *query = lua_tolstring(L, 2, &length);
222            if (query != NULL) {
223                int error = mysqllib_state.mysql_real_query(data->db, query, (int) length);
224                if (!error) {
225                    mysql_result * result = mysqllib_state.mysql_store_result(data->db);
226                    if (result != NULL) {
227                        int nofrows = 0;
228                        int nofcolumns = 0;
229                        mysqllib_state.mysql_field_seek(result, 0);
230                        nofrows = (int) mysqllib_state.mysql_num_rows(result);
231                        nofcolumns = mysqllib_state.mysql_num_fields(result);
232                        /* This is similar to sqlite but there the callback is more indirect. */
233                        if (nofcolumns > 0 && nofrows > 0) {
234                            for (int r = 0; r < nofrows; r++) {
235                                mysql_row row = mysqllib_state.mysql_fetch_row(result);
236                                lua_pushvalue(L, -1);
237                                lua_pushinteger(L, nofcolumns);
238                                lua_createtable(L, nofcolumns, 0);
239                                for (int c = 0; c < nofcolumns; c++) {
240                                    lua_pushstring(L, row[c]);
241                                    lua_rawseti(L, -2, (lua_Integer)c + 1);
242                                }
243                                if (r) {
244                                    lua_call(L, 2, 0);
245                                } else {
246                                    mysql_field * fields = mysqllib_state.mysql_fetch_fields(result);
247                                    lua_createtable(L, nofcolumns, 0);
248                                    for (int c = 0; c < nofcolumns; c++) {
249                                        lua_pushstring(L, fields[c].name);
250                                        lua_rawseti(L, -2, (lua_Integer)c + 1);
251                                    }
252                                    lua_call(L, 3, 0);
253                                }
254                            }
255                        }
256                        mysqllib_state.mysql_free_result(result);
257                    }
258                    lua_pushboolean(L, 1);
259                    return 1;
260                }
261            }
262        }
263    }
264    lua_pushboolean(L, 0);
265    return 1;
266}
267
268static int mysqllib_getmessage(lua_State * L)
269{
270     if (mysqllib_state.initialized) {
271         mysqllib_data * data = luaL_checkudata(L, 1, MYSQLLIB_METATABLE);
272         if (data != NULL) {
273             lua_pushstring(L, mysqllib_state.mysql_error(data->db));
274             return 1;
275         }
276     }
277     return 0;
278}
279
280/* private */
281
282static int mysqllib_free(lua_State * L)
283{
284    return mysqllib_close(L);
285}
286
287/* <string> = tostring(instance) */
288
289static int mysqllib_tostring(lua_State * L)
290{
291    if (mysqllib_state.initialized) {
292        mysqllib_data * data = luaL_checkudata(L, 1, MYSQLLIB_METATABLE);
293        if (data != NULL) {
294            (void) lua_pushfstring(L, "<mysqllib-instance %p>", data);
295        } else {
296            lua_pushnil(L);
297        }
298        return 1;
299    } else {
300        return 0;
301    }
302}
303
304static const struct luaL_Reg mysqllib_metatable[] = {
305    { "__tostring", mysqllib_tostring },
306    { "__gc",       mysqllib_free     },
307    { NULL,         NULL              },
308};
309
310static struct luaL_Reg mysqllib_function_list[] = {
311    { "initialize", mysqllib_initialize },
312    { "open",       mysqllib_open       },
313    { "close",      mysqllib_close      },
314    { "execute",    mysqllib_execute    },
315    { "getmessage", mysqllib_getmessage },
316    { NULL,         NULL                },
317};
318
319int luaopen_mysql(lua_State * L)
320{
321    luaL_newmetatable(L, MYSQLLIB_METATABLE);
322    luaL_setfuncs(L, mysqllib_metatable, 0);
323    lmt_library_register(L, "mysql", mysqllib_function_list);
324    return 0;
325}
326