lmtpostgress.c /size: 9636 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 void PGconn;
9typedef void PGresult;
10
11typedef enum postgres_polling_status_type {
12    PGRES_POLLING_FAILED,
13    PGRES_POLLING_READING,
14    PGRES_POLLING_WRITING,
15    PGRES_POLLING_OK
16} postgres_polling_status_type;
17
18typedef enum postgres_exec_status_type {
19    PGRES_EMPTY_QUERY,
20    PGRES_COMMAND_OK,
21    PGRES_TUPLES_OK,
22    PGRES_COPY_OUT,
23    PGRES_COPY_IN,
24    PGRES_BAD_RESPONSE,
25    PGRES_NONFATAL_ERROR,
26    PGRES_FATAL_ERROR,
27    PGRES_COPY_BOTH,
28    PGRES_SINGLE_TUPLE
29} postgres_exec_status_type;
30
31typedef enum postgres_connection_status_type {
32    PGRES_CONNECTION_OK,
33    PGRES_CONNECTION_BAD,
34    PGRES_CONNECTION_STARTED,
35    PGRES_CONNECTION_MADE,
36    PGRES_CONNECTION_AWAITING_RESPONSE,
37    PGRES_CONNECTION_AUTH_OK,
38    PGRES_CONNECTION_SETENV,
39    PGRES_CONNECTION_SSL_STARTUP,
40    PGRES_CONNECTION_NEEDED
41} postgres_connection_status_type;
42
43# define POSTGRESSLIB_METATABLE  "luatex.postgresslib"
44
45typedef struct postgresslib_data {
46    /*tex There is not much more than a pointer currently. */
47    PGconn * db;
48} postgresslib_data ;
49
50typedef struct postgresslib_state_info {
51
52    int initialized;
53    int padding;
54
55    PGconn * (*PQsetdbLogin) (
56        const char *pghost,
57        const char *pgport,
58        const char *pgoptions,
59        const char *pgtty,
60        const char *dbName,
61        const char *login,
62        const char *pwd
63    );
64
65    postgres_connection_status_type (*PQstatus) (
66        const PGconn *conn
67    );
68
69    void (*PQfinish) (
70        PGconn *conn
71    );
72
73    char * (*PQerrorMessage) (
74        const PGconn *conn
75    );
76
77    int (*PQsendQuery) (
78        PGconn     *conn,
79        const char *command
80    );
81
82    PGresult * (*PQgetResult) (
83        PGconn *conn
84    );
85
86    postgres_exec_status_type (*PQresultStatus) (
87        const PGresult *res
88    );
89
90    int (*PQntuples) (
91        const PGresult *res
92    );
93
94    int (*PQnfields) (
95        const PGresult *res
96    );
97
98    void (*PQclear) (
99        PGresult *res
100    );
101
102    char * (*PQfname) (
103        const PGresult *res,
104        int             column_number
105    );
106
107    char * (*PQgetvalue) (
108        const PGresult *res,
109        int             row_number,
110        int             column_number
111    );
112
113} postgresslib_state_info;
114
115static postgresslib_state_info postgresslib_state = {
116
117    .initialized    = 0,
118    .padding        = 0,
119
120    .PQsetdbLogin   = NULL,
121    .PQstatus       = NULL,
122    .PQfinish       = NULL,
123    .PQerrorMessage = NULL,
124    .PQsendQuery    = NULL,
125    .PQgetResult    = NULL,
126    .PQresultStatus = NULL,
127    .PQntuples      = NULL,
128    .PQnfields      = NULL,
129    .PQclear        = NULL,
130    .PQfname        = NULL,
131    .PQgetvalue     = NULL,
132
133};
134
135static int postgresslib_initialize(lua_State * L)
136{
137    if (! postgresslib_state.initialized) {
138        const char *filename = lua_tostring(L, 1);
139        if (filename != NULL) {
140
141            lmt_library lib = lmt_library_load(filename);
142
143            postgresslib_state.PQsetdbLogin   = lmt_library_find(lib, "PQsetdbLogin");
144            postgresslib_state.PQstatus       = lmt_library_find(lib, "PQstatus");
145            postgresslib_state.PQfinish       = lmt_library_find(lib, "PQfinish");
146            postgresslib_state.PQerrorMessage = lmt_library_find(lib, "PQerrorMessage");
147            postgresslib_state.PQsendQuery    = lmt_library_find(lib, "PQsendQuery");
148            postgresslib_state.PQgetResult    = lmt_library_find(lib, "PQgetResult");
149            postgresslib_state.PQresultStatus = lmt_library_find(lib, "PQresultStatus");
150            postgresslib_state.PQntuples      = lmt_library_find(lib, "PQntuples");
151            postgresslib_state.PQnfields      = lmt_library_find(lib, "PQnfields");
152            postgresslib_state.PQclear        = lmt_library_find(lib, "PQclear");
153            postgresslib_state.PQfname        = lmt_library_find(lib, "PQfname");
154            postgresslib_state.PQgetvalue     = lmt_library_find(lib, "PQgetvalue");
155
156            postgresslib_state.initialized = lmt_library_okay(lib);
157        }
158    }
159    lua_pushboolean(L, postgresslib_state.initialized);
160    return 1;
161}
162
163static int postgresslib_open(lua_State * L)
164{
165    if (postgresslib_state.initialized) {
166        const char *database  = luaL_checkstring(L, 1);
167        const char *username  = luaL_optstring(L, 2, NULL);
168        const char *password  = luaL_optstring(L, 3, NULL);
169        const char *host      = luaL_optstring(L, 4, NULL);
170        const char *port      = luaL_optstring(L, 5, NULL);
171        PGconn     *db        = postgresslib_state.PQsetdbLogin(host, port, NULL, NULL, database, username, password);
172        if (db != NULL && postgresslib_state.PQstatus(db) == PGRES_CONNECTION_BAD) {
173            postgresslib_state.PQfinish(db);
174        } else {
175            postgresslib_data *data = lua_newuserdatauv(L, sizeof(data), 0);
176            data->db = db ;
177            luaL_getmetatable(L, POSTGRESSLIB_METATABLE);
178            lua_setmetatable(L, -2);
179            return 1;
180        }
181    }
182    return 0;
183}
184
185static int postgresslib_close(lua_State * L)
186{
187    if (postgresslib_state.initialized) {
188        postgresslib_data *data = luaL_checkudata(L, 1, POSTGRESSLIB_METATABLE);
189        if (data != NULL) {
190            postgresslib_state.PQfinish(data->db);
191            data->db = NULL;
192        }
193    }
194    return 0;
195}
196
197/* execute(database,querystring,callback) : callback(nofcolumns,values,fields)  */
198
199static int postgresslib_execute(lua_State * L)
200{
201    if (postgresslib_state.initialized) {
202        postgresslib_data *data = luaL_checkudata(L, 1, POSTGRESSLIB_METATABLE);
203        if (data != NULL) {
204            size_t length = 0;
205            const char *query = lua_tolstring(L, 2, &length);
206            if (query != NULL) {
207                int error = postgresslib_state.PQsendQuery(data->db, query);
208                if (!error) {
209                    PGresult * result = postgresslib_state.PQgetResult(data->db);
210                    if (result) {
211                        if (postgresslib_state.PQresultStatus(result) == PGRES_TUPLES_OK) {
212                            int nofrows    = postgresslib_state.PQntuples(result);
213                            int nofcolumns = postgresslib_state.PQnfields(result);
214                            /* This is similar to sqlite but there the callback is more indirect. */
215                            if (nofcolumns > 0 && nofrows > 0) {
216                                for (int r = 0; r < nofrows; r++) {
217                                    lua_pushvalue(L, -1);
218                                    lua_pushinteger(L, nofcolumns);
219                                    lua_createtable(L, nofcolumns, 0);
220                                    for (int c = 0; c < nofcolumns; c++) {
221                                        lua_pushstring(L, postgresslib_state.PQgetvalue(result, r, c));
222                                        lua_rawseti(L,- 2, (lua_Integer)c + 1);
223                                    }
224                                    if (r) {
225                                        lua_call(L, 2, 0);
226                                    } else {
227                                        lua_createtable(L, nofcolumns, 0);
228                                        for (int c = 0; c < nofcolumns; c++) {
229                                            lua_pushstring(L, postgresslib_state.PQfname(result,c));
230                                            lua_rawseti(L, -2, (lua_Integer)c + 1);
231                                        }
232                                        lua_call(L,3,0);
233                                   }
234                                }
235                            }
236                        }
237                        postgresslib_state.PQclear(result);
238                    }
239                    lua_pushboolean(L, 1);
240                    return 1;
241                }
242            }
243        }
244    }
245    lua_pushboolean(L, 0);
246    return 1;
247}
248
249static int postgresslib_getmessage(lua_State * L)
250{
251    if (postgresslib_state.initialized) {
252        postgresslib_data *data = luaL_checkudata(L, 1, POSTGRESSLIB_METATABLE);
253        if (data != NULL) {
254            lua_pushstring(L, postgresslib_state.PQerrorMessage(data->db));
255            return 1;
256        }
257    }
258    return 0;
259}
260
261/* private */
262
263static int postgresslib_free(lua_State * L)
264{
265    return postgresslib_close(L);
266}
267
268/* <string> = tostring(instance) */
269
270static int postgresslib_tostring(lua_State * L)
271 {
272    if (postgresslib_state.initialized) {
273        postgresslib_data *data = luaL_checkudata(L, 1, POSTGRESSLIB_METATABLE);
274        if (data != NULL) {
275            (void) lua_pushfstring(L, "<postgresslib-instance %p>", data);
276        } else {
277            lua_pushnil(L);
278        }
279        return 1;
280    } else {
281        return 0;
282    }
283}
284
285static const struct luaL_Reg postgresslib_metatable[] = {
286    { "__tostring", postgresslib_tostring },
287    { "__gc",       postgresslib_free     },
288    { NULL,         NULL                  },
289};
290
291static struct luaL_Reg postgresslib_function_list[] = {
292    { "initialize", postgresslib_initialize },
293    { "open",       postgresslib_open       },
294    { "close",      postgresslib_close      },
295    { "execute",    postgresslib_execute    },
296    { "getmessage", postgresslib_getmessage },
297    { NULL,         NULL                    },
298};
299
300int luaopen_postgress(lua_State * L)
301{
302    luaL_newmetatable(L, POSTGRESSLIB_METATABLE);
303    luaL_setfuncs(L, postgresslib_metatable, 0);
304    lmt_library_register(L, "postgress", postgresslib_function_list);
305    return 0;
306}
307