|
1 #include "dbd_mysql.h" |
|
2 |
|
3 static lua_push_type_t mysql_to_lua_push(unsigned int mysql_type) { |
|
4 lua_push_type_t lua_type; |
|
5 |
|
6 switch(mysql_type) { |
|
7 case MYSQL_TYPE_NULL: |
|
8 lua_type = LUA_PUSH_NIL; |
|
9 break; |
|
10 |
|
11 case MYSQL_TYPE_TINY: |
|
12 case MYSQL_TYPE_SHORT: |
|
13 case MYSQL_TYPE_LONG: |
|
14 lua_type = LUA_PUSH_INTEGER; |
|
15 break; |
|
16 |
|
17 case MYSQL_TYPE_DOUBLE: |
|
18 case MYSQL_TYPE_LONGLONG: |
|
19 lua_type = LUA_PUSH_NUMBER; |
|
20 break; |
|
21 |
|
22 default: |
|
23 lua_type = LUA_PUSH_STRING; |
|
24 } |
|
25 |
|
26 return lua_type; |
|
27 } |
|
28 |
|
29 static int statement_close(lua_State *L) { |
|
30 statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); |
|
31 |
|
32 if (statement->metadata) { |
|
33 mysql_free_result(statement->metadata); |
|
34 } |
|
35 |
|
36 if (statement->stmt) { |
|
37 mysql_stmt_close(statement->stmt); |
|
38 } |
|
39 |
|
40 return 1; |
|
41 } |
|
42 |
|
43 static int statement_execute(lua_State *L) { |
|
44 int n = lua_gettop(L); |
|
45 statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); |
|
46 int num_bind_params = n - 1; |
|
47 |
|
48 MYSQL_BIND *bind = NULL; |
|
49 MYSQL_RES *metadata = NULL; |
|
50 |
|
51 char *error_message = NULL; |
|
52 |
|
53 int p; |
|
54 |
|
55 bind = malloc(sizeof(MYSQL_BIND) * num_bind_params); |
|
56 memset(bind, 0, sizeof(MYSQL_BIND) * num_bind_params); |
|
57 |
|
58 for (p = 2; p <= n; p++) { |
|
59 int type = lua_type(L, p); |
|
60 int i = p - 2; |
|
61 |
|
62 const char *str = NULL; |
|
63 size_t str_len; |
|
64 |
|
65 double num; |
|
66 |
|
67 switch(type) { |
|
68 case LUA_TNIL: |
|
69 bind[i].buffer_type = MYSQL_TYPE_NULL; |
|
70 bind[i].is_null = (my_bool*)1; |
|
71 break; |
|
72 |
|
73 case LUA_TNUMBER: |
|
74 num = luaL_checknumber(L, p); |
|
75 |
|
76 bind[i].buffer_type = MYSQL_TYPE_DOUBLE; |
|
77 bind[i].is_null = (my_bool*)0; |
|
78 bind[i].buffer = (char *)# |
|
79 bind[i].length = 0; |
|
80 break; |
|
81 |
|
82 case LUA_TSTRING: |
|
83 str = luaL_checklstring(L, p, &str_len); |
|
84 |
|
85 bind[i].buffer_type = MYSQL_TYPE_STRING; |
|
86 bind[i].is_null = (my_bool*)0; |
|
87 bind[i].buffer = (char *)str; |
|
88 bind[i].length = &str_len; |
|
89 break; |
|
90 |
|
91 default: |
|
92 error_message = "Binding unknown or unsupported type"; |
|
93 goto cleanup; |
|
94 } |
|
95 } |
|
96 |
|
97 if (mysql_stmt_bind_param(statement->stmt, bind)) { |
|
98 error_message = "Error binding statement parameters: %s"; |
|
99 goto cleanup; |
|
100 } |
|
101 |
|
102 if (mysql_stmt_execute(statement->stmt)) { |
|
103 error_message = "Error executing statement: %s"; |
|
104 goto cleanup; |
|
105 } |
|
106 |
|
107 metadata = mysql_stmt_result_metadata(statement->stmt); |
|
108 |
|
109 cleanup: |
|
110 if (bind) |
|
111 free(bind); |
|
112 |
|
113 if (error_message) { |
|
114 luaL_error(L, error_message, mysql_stmt_error(statement->stmt)); |
|
115 return 0; |
|
116 } |
|
117 |
|
118 statement->metadata = metadata; |
|
119 |
|
120 return 1; |
|
121 } |
|
122 |
|
123 static int statement_fetch_impl(lua_State *L, int named_columns) { |
|
124 statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); |
|
125 int column_count; |
|
126 MYSQL_BIND *bind = NULL; |
|
127 const char *error_message = NULL; |
|
128 |
|
129 if (!statement->stmt) { |
|
130 luaL_error(L, "fetch called before execute"); |
|
131 lua_pushnil(L); |
|
132 return 1; |
|
133 } |
|
134 |
|
135 if (!statement->metadata) { |
|
136 lua_pushnil(L); |
|
137 return 1; |
|
138 } |
|
139 |
|
140 column_count = mysql_num_fields(statement->metadata); |
|
141 |
|
142 if (column_count > 0) { |
|
143 int i; |
|
144 MYSQL_FIELD *fields; |
|
145 |
|
146 bind = malloc(sizeof(MYSQL_BIND) * column_count); |
|
147 memset(bind, 0, sizeof(MYSQL_BIND) * column_count); |
|
148 |
|
149 fields = mysql_fetch_fields(statement->metadata); |
|
150 |
|
151 for (i = 0; i < column_count; i++) { |
|
152 unsigned int length = fields[i].length; |
|
153 |
|
154 char *buffer = (char *)malloc(length); |
|
155 memset(buffer, 0, length); |
|
156 |
|
157 bind[i].buffer_type = fields[i].type; |
|
158 bind[i].buffer = buffer; |
|
159 bind[i].buffer_length = length; |
|
160 } |
|
161 |
|
162 if (mysql_stmt_bind_result(statement->stmt, bind)) { |
|
163 error_message = "Error binding results: %s"; |
|
164 goto cleanup; |
|
165 } |
|
166 |
|
167 if (!mysql_stmt_fetch(statement->stmt)) { |
|
168 int d = 1; |
|
169 |
|
170 lua_newtable(L); |
|
171 for (i = 0; i < column_count; i++) { |
|
172 lua_push_type_t lua_push = mysql_to_lua_push(fields[i].type); |
|
173 const char *name = fields[i].name; |
|
174 |
|
175 if (lua_push == LUA_PUSH_NIL) { |
|
176 if (named_columns) { |
|
177 LUA_PUSH_ATTRIB_NIL(name); |
|
178 } else { |
|
179 LUA_PUSH_ARRAY_NIL(d); |
|
180 } |
|
181 } else if (lua_push == LUA_PUSH_INTEGER) { |
|
182 if (named_columns) { |
|
183 LUA_PUSH_ATTRIB_INT(name, *(int *)(bind[i].buffer)); |
|
184 } else { |
|
185 LUA_PUSH_ARRAY_INT(d, *(int *)(bind[i].buffer)); |
|
186 } |
|
187 } else if (lua_push == LUA_PUSH_NUMBER) { |
|
188 if (named_columns) { |
|
189 LUA_PUSH_ATTRIB_FLOAT(name, *(double *)(bind[i].buffer)); |
|
190 } else { |
|
191 LUA_PUSH_ARRAY_FLOAT(d, *(double *)(bind[i].buffer)); |
|
192 } |
|
193 } else if (lua_push == LUA_PUSH_STRING) { |
|
194 if (named_columns) { |
|
195 LUA_PUSH_ATTRIB_STRING(name, bind[i].buffer); |
|
196 } else { |
|
197 LUA_PUSH_ARRAY_STRING(d, bind[i].buffer); |
|
198 } |
|
199 } else if (lua_push == LUA_PUSH_BOOLEAN) { |
|
200 if (named_columns) { |
|
201 LUA_PUSH_ATTRIB_BOOL(name, *(int *)(bind[i].buffer)); |
|
202 } else { |
|
203 LUA_PUSH_ARRAY_BOOL(d, *(int *)(bind[i].buffer)); |
|
204 } |
|
205 } else { |
|
206 luaL_error(L, "Unknown push type in result set"); |
|
207 } |
|
208 } |
|
209 } else { |
|
210 lua_pushnil(L); |
|
211 } |
|
212 } |
|
213 |
|
214 cleanup: |
|
215 if (bind) { |
|
216 int i; |
|
217 |
|
218 for (i = 0; i < column_count; i++) { |
|
219 free(bind[i].buffer); |
|
220 } |
|
221 |
|
222 free(bind); |
|
223 } |
|
224 |
|
225 if (error_message) { |
|
226 luaL_error(L, error_message, mysql_stmt_error(statement->stmt)); |
|
227 return 0; |
|
228 } |
|
229 |
|
230 return 1; |
|
231 } |
|
232 |
|
233 |
|
234 static int statement_fetch(lua_State *L) { |
|
235 return statement_fetch_impl(L, 0); |
|
236 } |
|
237 |
|
238 static int statement_fetchtable(lua_State *L) { |
|
239 return statement_fetch_impl(L, 1); |
|
240 } |
|
241 |
|
242 static int statement_gc(lua_State *L) { |
|
243 /* always free the handle */ |
|
244 statement_close(L); |
|
245 |
|
246 return 0; |
|
247 } |
|
248 |
|
249 |
|
250 static const luaL_Reg statement_methods[] = { |
|
251 {"close", statement_close}, |
|
252 {"execute", statement_execute}, |
|
253 {"fetch", statement_fetch}, |
|
254 {"fetchtable", statement_fetchtable}, |
|
255 {NULL, NULL} |
|
256 }; |
|
257 |
|
258 static const luaL_Reg statement_class_methods[] = { |
|
259 {NULL, NULL} |
|
260 }; |
|
261 |
|
262 int dbd_mysql_statement_create(lua_State *L, connection_t *conn, const char *sql_query) { |
|
263 unsigned long sql_len = strlen(sql_query); |
|
264 |
|
265 statement_t *statement = NULL; |
|
266 |
|
267 MYSQL_STMT *stmt = mysql_stmt_init(conn->mysql); |
|
268 |
|
269 if (!stmt) { |
|
270 luaL_error(L, "Error allocating statement handle: %s", mysql_error(conn->mysql)); |
|
271 return 0; |
|
272 } |
|
273 |
|
274 if (mysql_stmt_prepare(stmt, sql_query, sql_len)) { |
|
275 luaL_error(L, "Error preparing statement handle: %s", mysql_stmt_error(stmt)); |
|
276 return 0; |
|
277 } |
|
278 |
|
279 statement = (statement_t *)lua_newuserdata(L, sizeof(statement_t)); |
|
280 statement->mysql = conn->mysql; |
|
281 statement->stmt = stmt; |
|
282 statement->metadata = NULL; |
|
283 |
|
284 luaL_getmetatable(L, DBD_MYSQL_STATEMENT); |
|
285 lua_setmetatable(L, -2); |
|
286 |
|
287 return 1; |
|
288 } |
|
289 |
|
290 int dbd_mysql_statement(lua_State *L) { |
|
291 luaL_newmetatable(L, DBD_MYSQL_STATEMENT); |
|
292 luaL_register(L, 0, statement_methods); |
|
293 lua_pushvalue(L,-1); |
|
294 lua_setfield(L, -2, "__index"); |
|
295 |
|
296 lua_pushcfunction(L, statement_gc); |
|
297 lua_setfield(L, -2, "__gc"); |
|
298 |
|
299 luaL_register(L, DBD_MYSQL_STATEMENT, statement_class_methods); |
|
300 |
|
301 return 1; |
|
302 } |