1 #include "dbd_postgresql.h" |
1 #include "dbd_postgresql.h" |
2 |
2 |
3 #define MAX_PLACEHOLDERS 9999 |
3 #define MAX_PLACEHOLDERS 9999 |
4 #define MAX_PLACEHOLDER_SIZE (1+4) /* $\d{4} */ |
4 #define MAX_PLACEHOLDER_SIZE (1+4) /* $\d{4} */ |
5 |
5 |
6 static lua_push_type_t postgresql_to_lua_push(unsigned int postgresql_type) { |
6 static lua_push_type_t postgresql_to_lua_push(unsigned int postgresql_type) { |
7 lua_push_type_t lua_type; |
7 lua_push_type_t lua_type; |
8 |
8 |
26 } |
26 } |
27 |
27 |
28 return lua_type; |
28 return lua_type; |
29 } |
29 } |
30 |
30 |
|
31 /* |
|
32 * replace '?' placeholders with $\d+ placeholders |
|
33 * to be compatible with PSQL API |
|
34 */ |
31 static char *replace_placeholders(lua_State *L, const char *sql) { |
35 static char *replace_placeholders(lua_State *L, const char *sql) { |
32 size_t len = strlen(sql); |
36 size_t len = strlen(sql); |
33 int num_placeholders = 0; |
37 int num_placeholders = 0; |
34 int extra_space = 0; |
38 int extra_space = 0; |
35 int i; |
39 int i; |
36 char *newsql; |
40 char *newsql; |
37 int newpos = 1; |
41 int newpos = 1; |
38 int ph_num = 1; |
42 int ph_num = 1; |
39 int in_quote = 0; |
43 int in_quote = 0; |
40 |
44 |
|
45 /* |
|
46 * dumb count of all '?' |
|
47 * this will match more placeholders than necessesary |
|
48 * but it's safer to allocate more placeholders at the |
|
49 * cost of a few bytes than risk a buffer overflow |
|
50 */ |
41 for (i = 1; i < len; i++) { |
51 for (i = 1; i < len; i++) { |
42 if (sql[i] == '?') { |
52 if (sql[i] == '?') { |
43 num_placeholders++; |
53 num_placeholders++; |
44 } |
54 } |
45 } |
55 } |
46 |
56 |
|
57 /* |
|
58 * this is MAX_PLACEHOLDER_SIZE-1 because the '?' is |
|
59 * replaced with '$' |
|
60 */ |
47 extra_space = num_placeholders * (MAX_PLACEHOLDER_SIZE-1); |
61 extra_space = num_placeholders * (MAX_PLACEHOLDER_SIZE-1); |
48 |
62 |
|
63 /* |
|
64 * allocate a new string for the converted SQL statement |
|
65 */ |
49 newsql = malloc(sizeof(char) * (len+extra_space+1)); |
66 newsql = malloc(sizeof(char) * (len+extra_space+1)); |
50 memset(newsql, 0, sizeof(char) * (len+extra_space+1)); |
67 memset(newsql, 0, sizeof(char) * (len+extra_space+1)); |
51 |
68 |
52 /* copy first char */ |
69 /* |
|
70 * copy first char. In valid SQL this cannot be a placeholder |
|
71 */ |
53 newsql[0] = sql[0]; |
72 newsql[0] = sql[0]; |
|
73 |
|
74 /* |
|
75 * only replace '?' not in a single quoted string |
|
76 */ |
54 for (i = 1; i < len; i++) { |
77 for (i = 1; i < len; i++) { |
|
78 /* |
|
79 * don't change the quote flag if the ''' is preceded |
|
80 * bt a '\' to account for escaping |
|
81 */ |
55 if (sql[i] == '\'' && sql[i-1] != '\\') { |
82 if (sql[i] == '\'' && sql[i-1] != '\\') { |
56 in_quote = !in_quote; |
83 in_quote = !in_quote; |
57 } |
84 } |
58 |
85 |
59 if (sql[i] == '?' && !in_quote) { |
86 if (sql[i] == '?' && !in_quote) { |
70 newsql[newpos] = sql[i]; |
97 newsql[newpos] = sql[i]; |
71 newpos++; |
98 newpos++; |
72 } |
99 } |
73 } |
100 } |
74 |
101 |
75 /* terminate string on the last position */ |
102 /* |
|
103 * terminate string on the last position |
|
104 */ |
76 newsql[newpos] = '\0'; |
105 newsql[newpos] = '\0'; |
77 |
106 |
78 /* fprintf(stderr, "[%s]\n", newsql); */ |
107 /* fprintf(stderr, "[%s]\n", newsql); */ |
79 return newsql; |
108 return newsql; |
80 } |
109 } |
81 |
110 |
|
111 /* |
|
112 * success = statement:close() |
|
113 */ |
82 static int statement_close(lua_State *L) { |
114 static int statement_close(lua_State *L) { |
83 statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT); |
115 statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT); |
84 |
116 |
85 if (statement->result) { |
117 if (statement->result) { |
86 PQclear(statement->result); |
118 PQclear(statement->result); |
88 } |
120 } |
89 |
121 |
90 return 0; |
122 return 0; |
91 } |
123 } |
92 |
124 |
|
125 /* |
|
126 * success = statement:execute(...) |
|
127 */ |
93 static int statement_execute(lua_State *L) { |
128 static int statement_execute(lua_State *L) { |
94 int n = lua_gettop(L); |
129 int n = lua_gettop(L); |
95 statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT); |
130 statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT); |
96 int num_bind_params = n - 1; |
131 int num_bind_params = n - 1; |
97 ExecStatusType status; |
132 ExecStatusType status; |
155 |
196 |
156 lua_pushboolean(L, 1); |
197 lua_pushboolean(L, 1); |
157 return 1; |
198 return 1; |
158 } |
199 } |
159 |
200 |
|
201 /* |
|
202 * must be called after an execute |
|
203 */ |
160 static int statement_fetch_impl(lua_State *L, int named_columns) { |
204 static int statement_fetch_impl(lua_State *L, int named_columns) { |
161 statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT); |
205 statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT); |
162 int tuple = statement->tuple++; |
206 int tuple = statement->tuple++; |
163 int i; |
207 int i; |
164 int num_columns; |
208 int num_columns; |
187 } |
231 } |
188 } else { |
232 } else { |
189 const char *value = PQgetvalue(statement->result, tuple, i); |
233 const char *value = PQgetvalue(statement->result, tuple, i); |
190 lua_push_type_t lua_push = postgresql_to_lua_push(PQftype(statement->result, i)); |
234 lua_push_type_t lua_push = postgresql_to_lua_push(PQftype(statement->result, i)); |
191 |
235 |
|
236 /* |
|
237 * data is returned as strings from PSQL |
|
238 */ |
|
239 |
192 if (lua_push == LUA_PUSH_NIL) { |
240 if (lua_push == LUA_PUSH_NIL) { |
193 if (named_columns) { |
241 if (named_columns) { |
194 LUA_PUSH_ATTRIB_NIL(name); |
242 LUA_PUSH_ATTRIB_NIL(name); |
195 } else { |
243 } else { |
196 LUA_PUSH_ARRAY_NIL(d); |
244 LUA_PUSH_ARRAY_NIL(d); |
216 LUA_PUSH_ATTRIB_STRING(name, value); |
264 LUA_PUSH_ATTRIB_STRING(name, value); |
217 } else { |
265 } else { |
218 LUA_PUSH_ARRAY_STRING(d, value); |
266 LUA_PUSH_ARRAY_STRING(d, value); |
219 } |
267 } |
220 } else if (lua_push == LUA_PUSH_BOOLEAN) { |
268 } else if (lua_push == LUA_PUSH_BOOLEAN) { |
|
269 /* |
|
270 * booleans are returned as a string |
|
271 * either 't' or 'f' |
|
272 */ |
221 int val = value[0] == 't' ? 1 : 0; |
273 int val = value[0] == 't' ? 1 : 0; |
222 |
274 |
223 if (named_columns) { |
275 if (named_columns) { |
224 LUA_PUSH_ATTRIB_BOOL(name, val); |
276 LUA_PUSH_ATTRIB_BOOL(name, val); |
225 } else { |
277 } else { |
232 } |
284 } |
233 |
285 |
234 return 1; |
286 return 1; |
235 } |
287 } |
236 |
288 |
237 |
289 /* |
|
290 * array = statement:fetch() |
|
291 */ |
238 static int statement_fetch(lua_State *L) { |
292 static int statement_fetch(lua_State *L) { |
239 return statement_fetch_impl(L, 0); |
293 return statement_fetch_impl(L, 0); |
240 } |
294 } |
241 |
295 |
|
296 /* |
|
297 * hashmap = statement:fetchtable() |
|
298 */ |
242 static int statement_fetchtable(lua_State *L) { |
299 static int statement_fetchtable(lua_State *L) { |
243 return statement_fetch_impl(L, 1); |
300 return statement_fetch_impl(L, 1); |
244 } |
301 } |
245 |
302 |
|
303 /* |
|
304 * __gc |
|
305 */ |
246 static int statement_gc(lua_State *L) { |
306 static int statement_gc(lua_State *L) { |
247 /* always free the handle */ |
307 /* always free the handle */ |
248 statement_close(L); |
308 statement_close(L); |
249 |
309 |
250 return 0; |
310 return 0; |
251 } |
311 } |
252 |
|
253 |
|
254 static const luaL_Reg statement_methods[] = { |
|
255 {"close", statement_close}, |
|
256 {"execute", statement_execute}, |
|
257 {"fetch", statement_fetch}, |
|
258 {"fetchtable", statement_fetchtable}, |
|
259 {NULL, NULL} |
|
260 }; |
|
261 |
|
262 static const luaL_Reg statement_class_methods[] = { |
|
263 {NULL, NULL} |
|
264 }; |
|
265 |
312 |
266 int dbd_postgresql_statement_create(lua_State *L, connection_t *conn, const char *sql_query) { |
313 int dbd_postgresql_statement_create(lua_State *L, connection_t *conn, const char *sql_query) { |
267 statement_t *statement = NULL; |
314 statement_t *statement = NULL; |
268 ExecStatusType status; |
315 ExecStatusType status; |
269 PGresult *result = NULL; |
316 PGresult *result = NULL; |
270 char *new_sql; |
317 char *new_sql; |
271 char name[IDLEN]; |
318 char name[IDLEN]; |
272 |
319 |
|
320 /* |
|
321 * convert SQL string into a PSQL API compatible SQL statement |
|
322 */ |
273 new_sql = replace_placeholders(L, sql_query); |
323 new_sql = replace_placeholders(L, sql_query); |
274 |
324 |
275 snprintf(name, IDLEN, "%017u", ++conn->statement_id); |
325 snprintf(name, IDLEN, "%017u", ++conn->statement_id); |
276 |
326 |
277 result = PQprepare(conn->postgresql, name, new_sql, 0, NULL); |
327 result = PQprepare(conn->postgresql, name, new_sql, 0, NULL); |
|
328 |
|
329 /* |
|
330 * free converted statement after use |
|
331 */ |
278 free(new_sql); |
332 free(new_sql); |
279 |
333 |
280 if (!result) { |
334 if (!result) { |
281 luaL_error(L, "Unable to allocate prepare result handle"); |
335 luaL_error(L, "Unable to allocate prepare result handle"); |
282 } |
336 } |
303 |
357 |
304 return 1; |
358 return 1; |
305 } |
359 } |
306 |
360 |
307 int dbd_postgresql_statement(lua_State *L) { |
361 int dbd_postgresql_statement(lua_State *L) { |
|
362 static const luaL_Reg statement_methods[] = { |
|
363 {"close", statement_close}, |
|
364 {"execute", statement_execute}, |
|
365 {"fetch", statement_fetch}, |
|
366 {"fetchtable", statement_fetchtable}, |
|
367 {NULL, NULL} |
|
368 }; |
|
369 |
|
370 static const luaL_Reg statement_class_methods[] = { |
|
371 {NULL, NULL} |
|
372 }; |
|
373 |
308 luaL_newmetatable(L, DBD_POSTGRESQL_STATEMENT); |
374 luaL_newmetatable(L, DBD_POSTGRESQL_STATEMENT); |
309 luaL_register(L, 0, statement_methods); |
375 luaL_register(L, 0, statement_methods); |
310 lua_pushvalue(L,-1); |
376 lua_pushvalue(L,-1); |
311 lua_setfield(L, -2, "__index"); |
377 lua_setfield(L, -2, "__index"); |
312 |
378 |