dbd/postgresql/statement.c

1
#include "dbd_postgresql.h"
2
 
3
#define BOOLOID                 16
4
#define INT2OID                 21
5
#define INT4OID                 23
6
#define INT8OID                 20
7
#define FLOAT4OID		700
8
#define FLOAT8OID		701
9
 
10
static lua_push_type_t postgresql_to_lua_push(unsigned int postgresql_type) {
11
    lua_push_type_t lua_type;
12
 
13
    switch(postgresql_type) {
14
    case INT2OID:
15
    case INT4OID:
16
    case INT8OID:
17
        lua_type =  LUA_PUSH_INTEGER;
18
        break;
19
 
20
    case FLOAT4OID:
21
    case FLOAT8OID:
22
        lua_type = LUA_PUSH_NUMBER;
23
        break;
24
 
25
    case BOOLOID:
26
	    lua_type = LUA_PUSH_BOOLEAN;
27
	    break;
28
 
29
    default:
30
        lua_type = LUA_PUSH_STRING;
31
    }
32
 
33
    return lua_type;
34
}
35
 
36
static int deallocate(statement_t *statement) {
37
    char command[IDLEN+13];
38
	PGresult *result;
39
	ExecStatusType status;
40
 
41
	snprintf(command, IDLEN+13, "DEALLOCATE \"%s\"", statement->name);    
42
    result = PQexec(statement->postgresql, command);
43
 
44
    if (!result)
45
        return 1;
46
 
47
    status = PQresultStatus(result);
48
    PQclear(result);
49
 
50
    if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK)
51
        return 1;
52
 
53
    return 0;
54
}
55
 
56
 
57
/*
58
 * num_affected_rows = statement:affected()
59
 */
60
static int statement_affected(lua_State *L) {
61
    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
62
 
63
    if (!statement->result) {
64
        luaL_error(L, DBI_ERR_INVALID_STATEMENT);
65
    }
66
 
67
    lua_pushinteger(L, atoi(PQcmdTuples(statement->result)));
68
 
69
    return 1;
70
}
71
 
72
/*
73
 * success = statement:close()
74
 */
75
static int statement_close(lua_State *L) {
76
    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
77
 
78
    if (statement->result) {
79
        /*
80
         * Deallocate prepared statement on the
81
         * server side
82
         */ 
83
        deallocate(statement); 
84
 
85
	PQclear(statement->result);
86
	statement->result = NULL;
87
    }
88
 
89
    return 0;    
90
}
91
 
92
/*
93
 * column_names = statement:columns()
94
 */
95
static int statement_columns(lua_State *L) {
96
    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
97
 
98
    int i;
99
    int num_columns;
100
    int d = 1;
101
 
102
    if (!statement->result) {
103
        luaL_error(L, DBI_ERR_INVALID_STATEMENT);
104
        return 0;
105
    }
106
 
107
    num_columns = PQnfields(statement->result);
108
    lua_newtable(L);
109
    for (i = 0; i < num_columns; i++) {
110
        const char *name = PQfname(statement->result, i);
111
 
112
	LUA_PUSH_ARRAY_STRING(d, name);
113
    }
114
 
115
    return 1;
116
}
117
 
118
/*
119
 * success = statement:execute(...)
120
 */
121
static int statement_execute(lua_State *L) {
122
    int n = lua_gettop(L);
123
    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
124
    int num_bind_params = n - 1;   
125
    ExecStatusType status;
126
    int p;
127
    const char *errstr = NULL;
128
 
129
    const char **params;
130
    PGresult *result = NULL;
131
 
132
    statement->tuple = 0;
133
 
134
    params = malloc(num_bind_params * sizeof(params));
135
    memset(params, 0, num_bind_params * sizeof(params));
136
 
137
    /*
138
     * convert and copy parameters into a string array
139
     */ 
140
    for (p = 2; p <= n; p++) {
141
        int i = p - 2;	
142
        int type = lua_type(L, p);
143
        char err[64];
144
 
145
        switch(type) {
146
        case LUA_TNIL:
147
            params[i] = NULL;
148
            break;
149
        case LUA_TBOOLEAN:
150
            /*
151
             * boolean values in postgresql can either be
152
             * t/f or 1/0. Pass integer values rather than
153
             * strings to maintain semantic compatibility
154
             * with other DBD drivers that pass booleans
155
             * as integers.
156
             */
157
            params[i] = lua_toboolean(L, p) ?  "1" : "0";
158
            break;
159
        case LUA_TNUMBER:
160
        case LUA_TSTRING:
161
            params[i] = lua_tostring(L, p);
162
            break;
163
        default:
164
            snprintf(err, sizeof(err)-1, DBI_ERR_BINDING_TYPE_ERR, lua_typename(L, type));
165
            errstr = err;
166
            goto cleanup;
167
        }
168
    }
169
 
170
    result = PQexecPrepared(
171
        statement->postgresql,
172
        statement->name,
173
        num_bind_params,
174
        (const char **)params,
175
        NULL,
176
        NULL,
177
        0
178
    );
179
 
180
cleanup:
181
    free(params);
182
 
183
    if (errstr) {
184
        lua_pushboolean(L, 0);
185
        lua_pushfstring(L, DBI_ERR_BINDING_PARAMS, errstr);
186
        return 2;
187
    }
188
 
189
    if (!result) {
190
        lua_pushboolean(L, 0);
191
        lua_pushfstring(L, DBI_ERR_ALLOC_RESULT,  PQerrorMessage(statement->postgresql));
192
        return 2;
193
    }
194
    
195
    status = PQresultStatus(result);
196
    if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
197
        lua_pushboolean(L, 0);
198
        lua_pushfstring(L, DBI_ERR_BINDING_EXEC, PQresultErrorMessage(result));
199
        return 2;
200
    }
201
    
202
    statement->result = result;
203
 
204
    lua_pushboolean(L, 1);
205
    return 1;
206
}
207
 
208
/*
209
 * can only be called after an execute
210
 */
211
static int statement_fetch_impl(lua_State *L, statement_t *statement, int named_columns) {
212
    int tuple = statement->tuple++;
213
    int i;
214
    int num_columns;
215
    int d = 1;
216
 
217
    if (!statement->result) {
218
        luaL_error(L, DBI_ERR_FETCH_INVALID);
219
        return 0;
220
    }
221
 
222
    if (PQresultStatus(statement->result) != PGRES_TUPLES_OK) {
223
        lua_pushnil(L);
224
        return 1;
225
    }
226
 
227
    if (tuple >= PQntuples(statement->result)) {
228
        lua_pushnil(L);  /* no more results */
229
        return 1;
230
    }
231
 
232
    num_columns = PQnfields(statement->result);
233
    lua_newtable(L);
234
    for (i = 0; i < num_columns; i++) {
235
        const char *name = PQfname(statement->result, i);
236
 
237
        if (PQgetisnull(statement->result, tuple, i)) {
238
            if (named_columns) {
239
                LUA_PUSH_ATTRIB_NIL(name);
240
            } else {
241
                LUA_PUSH_ARRAY_NIL(d);
242
            }	    
243
        } else {
244
            const char *value = PQgetvalue(statement->result, tuple, i);
245
            lua_push_type_t lua_push = postgresql_to_lua_push(PQftype(statement->result, i));
246
 
247
            /*
248
             * data is returned as strings from PSQL
249
             * convert them here into Lua types
250
             */ 
251
 
252
            if (lua_push == LUA_PUSH_NIL) {
253
                if (named_columns) {
254
                    LUA_PUSH_ATTRIB_NIL(name);
255
                } else {
256
                    LUA_PUSH_ARRAY_NIL(d);
257
                }
258
            } else if (lua_push == LUA_PUSH_INTEGER) {
259
                int val = atoi(value);
260
 
261
                if (named_columns) {
262
                    LUA_PUSH_ATTRIB_INT(name, val);
263
                } else {
264
                    LUA_PUSH_ARRAY_INT(d, val);
265
                }
266
            } else if (lua_push == LUA_PUSH_NUMBER) {
267
                double val = strtod(value, NULL);
268
 
269
                if (named_columns) {
270
                    LUA_PUSH_ATTRIB_FLOAT(name, val);
271
                } else {
272
                    LUA_PUSH_ARRAY_FLOAT(d, val);
273
                }
274
            } else if (lua_push == LUA_PUSH_STRING) {
275
                if (named_columns) {
276
                    LUA_PUSH_ATTRIB_STRING(name, value);
277
                } else {
278
                    LUA_PUSH_ARRAY_STRING(d, value);
279
                }
280
            } else if (lua_push == LUA_PUSH_BOOLEAN) {
281
                /* 
282
                 * booleans are returned as a string
283
                 * either 't' or 'f'
284
                 */
285
                int val = value[0] == 't' ? 1 : 0;
286
 
287
                if (named_columns) {
288
                    LUA_PUSH_ATTRIB_BOOL(name, val);
289
                } else {
290
                    LUA_PUSH_ARRAY_BOOL(d, val);
291
                }
292
            } else {
293
                luaL_error(L, DBI_ERR_UNKNOWN_PUSH);
294
            }
295
        }
296
    }
297
 
298
    return 1;    
299
}
300
 
301
 
302
static int next_iterator(lua_State *L) {
303
    statement_t *statement = (statement_t *)luaL_checkudata(L, lua_upvalueindex(1), DBD_POSTGRESQL_STATEMENT);
304
    int named_columns = lua_toboolean(L, lua_upvalueindex(2));
305
 
306
    return statement_fetch_impl(L, statement, named_columns);
307
}
308
 
309
/*
310
 * table = statement:fetch(named_indexes)
311
 */
312
static int statement_fetch(lua_State *L) {
313
    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
314
    int named_columns = lua_toboolean(L, 2);
315
 
316
    return statement_fetch_impl(L, statement, named_columns);
317
}
318
 
319
/*
320
 * num_rows = statement:rowcount()
321
 */
322
static int statement_rowcount(lua_State *L) {
323
    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
324
 
325
    if (!statement->result) {
326
        luaL_error(L, DBI_ERR_INVALID_STATEMENT);
327
    }
328
 
329
    lua_pushinteger(L, PQntuples(statement->result));
330
 
331
    return 1;
332
}
333
 
334
/*
335
 * iterfunc = statement:rows(named_indexes)
336
 */
337
static int statement_rows(lua_State *L) {
338
    if (lua_gettop(L) == 1) {
339
        lua_pushvalue(L, 1);
340
        lua_pushboolean(L, 0);
341
    } else {
342
        lua_pushvalue(L, 1);
343
        lua_pushboolean(L, lua_toboolean(L, 2));
344
    }
345
 
346
    lua_pushcclosure(L, next_iterator, 2);
347
    return 1;
348
}
349
 
350
/*
351
 * __gc
352
 */
353
static int statement_gc(lua_State *L) {
354
    /* always free the handle */
355
    statement_close(L);
356
 
357
    return 0;
358
}
359
 
360
/*
361
 * __tostring
362
 */
363
static int statement_tostring(lua_State *L) {
364
    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
365
 
366
    lua_pushfstring(L, "%s: %p", DBD_POSTGRESQL_STATEMENT, statement);
367
 
368
    return 1;
369
}
370
 
371
int dbd_postgresql_statement_create(lua_State *L, connection_t *conn, const char *sql_query) { 
372
    statement_t *statement = NULL;
373
    ExecStatusType status;
374
    PGresult *result = NULL;
375
    char *new_sql;
376
    char name[IDLEN];
377
 
378
    /*
379
     * convert SQL string into a PSQL API compatible SQL statement
380
     */ 
381
    new_sql = replace_placeholders(L, '$', sql_query);
382
 
383
    snprintf(name, IDLEN, "dbd-postgresql-%017u", ++conn->statement_id);
384
 
385
    result = PQprepare(conn->postgresql, name, new_sql, 0, NULL);
386
 
387
    /*
388
     * free converted statement after use
389
     */
390
    free(new_sql);
391
 
392
    if (!result) {
393
        lua_pushnil(L);
394
        lua_pushfstring(L, DBI_ERR_ALLOC_STATEMENT, PQerrorMessage(statement->postgresql));
395
        return 2;
396
    }
397
    
398
    status = PQresultStatus(result);
399
    if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
400
        const char *err_string = PQresultErrorMessage(result);
401
        PQclear(result);
402
 
403
        lua_pushnil(L);
404
        lua_pushfstring(L, DBI_ERR_PREP_STATEMENT, err_string);
405
        return 2;
406
    }
407
 
408
    PQclear(result);
409
 
410
    statement = (statement_t *)lua_newuserdata(L, sizeof(statement_t));
411
    statement->postgresql = conn->postgresql;
412
    statement->result = NULL;
413
    statement->tuple = 0;
414
    strncpy(statement->name, name, IDLEN-1);
415
    statement->name[IDLEN-1] = '\0';
416
 
417
    luaL_getmetatable(L, DBD_POSTGRESQL_STATEMENT);
418
    lua_setmetatable(L, -2);
419
 
420
    return 1;
421
} 
422
 
423
int dbd_postgresql_statement(lua_State *L) {
424
    static const luaL_Reg statement_methods[] = {
425
        {"affected", statement_affected},
426
        {"close", statement_close},
427
        {"columns", statement_columns},
428
        {"execute", statement_execute},
429
        {"fetch", statement_fetch},
430
        {"rowcount", statement_rowcount},
431
        {"rows", statement_rows},
432
        {NULL, NULL}
433
    };
434
 
435
    static const luaL_Reg statement_class_methods[] = {
436
        {NULL, NULL}
437
    };
438
 
439
    luaL_newmetatable(L, DBD_POSTGRESQL_STATEMENT);
440
    luaL_register(L, 0, statement_methods);
441
    lua_pushvalue(L,-1);
442
    lua_setfield(L, -2, "__index");
443
 
444
    lua_pushcfunction(L, statement_gc);
445
    lua_setfield(L, -2, "__gc");
446
 
447
    lua_pushcfunction(L, statement_tostring);
448
    lua_setfield(L, -2, "__tostring");
449
 
450
    luaL_register(L, DBD_POSTGRESQL_STATEMENT, statement_class_methods);
451
 
452
    return 1;    
453
}