dbd/postgresql/statement.c

changeset 2
c4f02fc67e5a
parent 1
408291a6eb3e
child 3
b61020ca4753
equal deleted inserted replaced
1:408291a6eb3e 2:c4f02fc67e5a
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;
102 137
103 statement->tuple = 0; 138 statement->tuple = 0;
104 139
105 params = malloc(num_bind_params * sizeof(params)); 140 params = malloc(num_bind_params * sizeof(params));
106 141
142 /*
143 * convert and copy parameters into a string array
144 */
107 for (p = 2; p <= n; p++) { 145 for (p = 2; p <= n; p++) {
108 int i = p - 2; 146 int i = p - 2;
109 147
110 if (lua_isnil(L, p)) { 148 if (lua_isnil(L, p)) {
111 params[i] = NULL; 149 params[i] = NULL;
129 NULL, 167 NULL,
130 NULL, 168 NULL,
131 0 169 0
132 ); 170 );
133 171
172 /*
173 * free string array
174 */
134 for (p = 0; p < num_bind_params; p++) { 175 for (p = 0; p < num_bind_params; p++) {
135 if (params[p]) { 176 if (params[p]) {
136 free(params[p]); 177 free(params[p]);
137 } 178 }
138 } 179 }
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

mercurial