Fri, 29 Jun 2012 17:45:37 +0100
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
1 | 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: | |
33 | 12 | case MYSQL_TYPE_YEAR: |
1 | 13 | case MYSQL_TYPE_SHORT: |
14 | case MYSQL_TYPE_LONG: | |
15 | lua_type = LUA_PUSH_INTEGER; | |
16 | break; | |
17 | ||
18 | case MYSQL_TYPE_DOUBLE: | |
19 | case MYSQL_TYPE_LONGLONG: | |
20 | lua_type = LUA_PUSH_NUMBER; | |
21 | break; | |
22 | ||
23 | default: | |
24 | lua_type = LUA_PUSH_STRING; | |
25 | } | |
26 | ||
27 | return lua_type; | |
28 | } | |
29 | ||
33 | 30 | static size_t mysql_buffer_size(MYSQL_FIELD *field) { |
31 | unsigned int mysql_type = field->type; | |
32 | size_t size = 0; | |
33 | ||
34 | switch (mysql_type) { | |
35 | case MYSQL_TYPE_TINY: | |
36 | 36 | size = 1; |
33 | 37 | break; |
38 | case MYSQL_TYPE_YEAR: | |
39 | case MYSQL_TYPE_SHORT: | |
36 | 40 | size = 2; |
33 | 41 | break; |
42 | case MYSQL_TYPE_INT24: | |
43 | size = 4; | |
44 | break; | |
45 | case MYSQL_TYPE_LONG: | |
46 | size = 4; | |
47 | break; | |
48 | case MYSQL_TYPE_LONGLONG: | |
49 | size = 8; | |
50 | break; | |
51 | case MYSQL_TYPE_FLOAT: | |
52 | size = 4; | |
53 | break; | |
54 | case MYSQL_TYPE_DOUBLE: | |
55 | size = 8; | |
56 | break; | |
57 | case MYSQL_TYPE_TIME: | |
58 | case MYSQL_TYPE_DATE: | |
59 | case MYSQL_TYPE_DATETIME: | |
60 | case MYSQL_TYPE_TIMESTAMP: | |
61 | size = sizeof(MYSQL_TIME); | |
62 | break; | |
63 | default: | |
64 | size = field->length; | |
65 | } | |
66 | ||
67 | return size; | |
68 | } | |
69 | ||
2 | 70 | /* |
21
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
71 | * num_affected_rows = statement:affected() |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
72 | */ |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
73 | static int statement_affected(lua_State *L) { |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
74 | statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
75 | |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
76 | if (!statement->stmt) { |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
77 | luaL_error(L, DBI_ERR_INVALID_STATEMENT); |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
78 | } |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
79 | |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
80 | lua_pushinteger(L, mysql_stmt_affected_rows(statement->stmt)); |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
81 | |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
82 | return 1; |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
83 | } |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
84 | |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
85 | /* |
2 | 86 | * success = statement:close() |
87 | */ | |
1 | 88 | static int statement_close(lua_State *L) { |
89 | statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); | |
90 | ||
91 | if (statement->metadata) { | |
92 | mysql_free_result(statement->metadata); | |
3 | 93 | statement->metadata = NULL; |
1 | 94 | } |
95 | ||
96 | if (statement->stmt) { | |
3 | 97 | mysql_stmt_close(statement->stmt); |
98 | statement->stmt = NULL; | |
1 | 99 | } |
100 | ||
3 | 101 | lua_pushboolean(L, 1); |
1 | 102 | return 1; |
103 | } | |
104 | ||
2 | 105 | /* |
30
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
106 | * column_names = statement:columns() |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
107 | */ |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
108 | static int statement_columns(lua_State *L) { |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
109 | statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
110 | |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
111 | MYSQL_FIELD *fields; |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
112 | int i; |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
113 | int num_columns; |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
114 | int d = 1; |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
115 | |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
116 | if (!statement->stmt) { |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
117 | luaL_error(L, DBI_ERR_INVALID_STATEMENT); |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
118 | return 0; |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
119 | } |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
120 | |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
121 | fields = mysql_fetch_fields(statement->metadata); |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
122 | num_columns = mysql_num_fields(statement->metadata); |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
123 | lua_newtable(L); |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
124 | for (i = 0; i < num_columns; i++) { |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
125 | const char *name = fields[i].name; |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
126 | |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
127 | LUA_PUSH_ARRAY_STRING(d, name); |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
128 | } |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
129 | |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
130 | return 1; |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
131 | } |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
132 | |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
133 | |
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
134 | /* |
3 | 135 | * success,err = statement:execute(...) |
2 | 136 | */ |
1 | 137 | static int statement_execute(lua_State *L) { |
138 | int n = lua_gettop(L); | |
139 | statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); | |
140 | int num_bind_params = n - 1; | |
3 | 141 | int expected_params; |
14 | 142 | |
25 | 143 | unsigned char *buffer = NULL; |
14 | 144 | int offset = 0; |
1 | 145 | |
146 | MYSQL_BIND *bind = NULL; | |
147 | MYSQL_RES *metadata = NULL; | |
148 | ||
149 | char *error_message = NULL; | |
10 | 150 | char *errstr = NULL; |
1 | 151 | |
152 | int p; | |
153 | ||
36 | 154 | if (statement->metadata) { |
155 | /* | |
156 | * free existing metadata from any previous executions | |
157 | */ | |
158 | mysql_free_result(statement->metadata); | |
159 | statement->metadata = NULL; | |
160 | } | |
161 | ||
3 | 162 | if (!statement->stmt) { |
163 | lua_pushboolean(L, 0); | |
4 | 164 | lua_pushstring(L, DBI_ERR_EXECUTE_INVALID); |
3 | 165 | return 2; |
166 | } | |
167 | ||
168 | expected_params = mysql_stmt_param_count(statement->stmt); | |
169 | ||
170 | if (expected_params != num_bind_params) { | |
171 | /* | |
10 | 172 | * mysql_stmt_bind_param does not handle this condition, |
3 | 173 | * and the client library will segfault if these do no match |
174 | */ | |
175 | lua_pushboolean(L, 0); | |
4 | 176 | lua_pushfstring(L, DBI_ERR_PARAM_MISCOUNT, expected_params, num_bind_params); |
3 | 177 | return 2; |
178 | } | |
179 | ||
26 | 180 | if (num_bind_params > 0) { |
25 | 181 | bind = malloc(sizeof(MYSQL_BIND) * num_bind_params); |
182 | if (bind == NULL) { | |
183 | luaL_error(L, "Could not alloc bind params\n"); | |
184 | } | |
185 | ||
26 | 186 | buffer = (unsigned char *)malloc(num_bind_params * sizeof(double)); |
25 | 187 | memset(bind, 0, sizeof(MYSQL_BIND) * num_bind_params); |
23
a4825c3e65e9
Bugfix: memory corruption possible after reallocs. Using a static buffer instead. Will need to check for overflows on static buffer.
nrich@ii.net
parents:
21
diff
changeset
|
188 | } |
a4825c3e65e9
Bugfix: memory corruption possible after reallocs. Using a static buffer instead. Will need to check for overflows on static buffer.
nrich@ii.net
parents:
21
diff
changeset
|
189 | |
1 | 190 | for (p = 2; p <= n; p++) { |
191 | int type = lua_type(L, p); | |
192 | int i = p - 2; | |
193 | ||
194 | const char *str = NULL; | |
7
4480ae002881
Bugfix - the size of strings for bind params pointed to the same memory
nrich@ii.net
parents:
6
diff
changeset
|
195 | size_t *str_len = NULL; |
6
22046b996150
Fixed bug: double bind params were using the same variables memory space so they were all being set to the same number.
nrich@ii.net
parents:
4
diff
changeset
|
196 | double *num = NULL; |
9 | 197 | int *boolean = NULL; |
10 | 198 | char err[64]; |
1 | 199 | |
200 | switch(type) { | |
201 | case LUA_TNIL: | |
202 | bind[i].buffer_type = MYSQL_TYPE_NULL; | |
203 | bind[i].is_null = (my_bool*)1; | |
204 | break; | |
205 | ||
9 | 206 | case LUA_TBOOLEAN: |
26 | 207 | boolean = (int *)(buffer + offset); |
14 | 208 | offset += sizeof(int); |
9 | 209 | *boolean = lua_toboolean(L, p); |
14 | 210 | |
9 | 211 | bind[i].buffer_type = MYSQL_TYPE_LONG; |
212 | bind[i].is_null = (my_bool*)0; | |
213 | bind[i].buffer = (char *)boolean; | |
214 | bind[i].length = 0; | |
215 | break; | |
216 | ||
1 | 217 | case LUA_TNUMBER: |
6
22046b996150
Fixed bug: double bind params were using the same variables memory space so they were all being set to the same number.
nrich@ii.net
parents:
4
diff
changeset
|
218 | /* |
22046b996150
Fixed bug: double bind params were using the same variables memory space so they were all being set to the same number.
nrich@ii.net
parents:
4
diff
changeset
|
219 | * num needs to be it's own |
22046b996150
Fixed bug: double bind params were using the same variables memory space so they were all being set to the same number.
nrich@ii.net
parents:
4
diff
changeset
|
220 | * memory here |
22046b996150
Fixed bug: double bind params were using the same variables memory space so they were all being set to the same number.
nrich@ii.net
parents:
4
diff
changeset
|
221 | */ |
26 | 222 | num = (double *)(buffer + offset); |
14 | 223 | offset += sizeof(double); |
9 | 224 | *num = lua_tonumber(L, p); |
1 | 225 | |
226 | bind[i].buffer_type = MYSQL_TYPE_DOUBLE; | |
227 | bind[i].is_null = (my_bool*)0; | |
6
22046b996150
Fixed bug: double bind params were using the same variables memory space so they were all being set to the same number.
nrich@ii.net
parents:
4
diff
changeset
|
228 | bind[i].buffer = (char *)num; |
1 | 229 | bind[i].length = 0; |
230 | break; | |
231 | ||
232 | case LUA_TSTRING: | |
26 | 233 | str_len = (size_t *)(buffer + offset); |
14 | 234 | offset += sizeof(size_t); |
9 | 235 | str = lua_tolstring(L, p, str_len); |
1 | 236 | |
237 | bind[i].buffer_type = MYSQL_TYPE_STRING; | |
238 | bind[i].is_null = (my_bool*)0; | |
239 | bind[i].buffer = (char *)str; | |
7
4480ae002881
Bugfix - the size of strings for bind params pointed to the same memory
nrich@ii.net
parents:
6
diff
changeset
|
240 | bind[i].length = str_len; |
1 | 241 | break; |
242 | ||
243 | default: | |
10 | 244 | snprintf(err, sizeof(err)-1, DBI_ERR_BINDING_TYPE_ERR, lua_typename(L, type)); |
245 | errstr = err; | |
246 | error_message = DBI_ERR_BINDING_PARAMS; | |
1 | 247 | goto cleanup; |
248 | } | |
249 | } | |
250 | ||
251 | if (mysql_stmt_bind_param(statement->stmt, bind)) { | |
4 | 252 | error_message = DBI_ERR_BINDING_PARAMS; |
1 | 253 | goto cleanup; |
254 | } | |
255 | ||
256 | if (mysql_stmt_execute(statement->stmt)) { | |
4 | 257 | error_message = DBI_ERR_BINDING_EXEC; |
1 | 258 | goto cleanup; |
259 | } | |
260 | ||
261 | metadata = mysql_stmt_result_metadata(statement->stmt); | |
262 | ||
21
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
263 | if (metadata) { |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
264 | mysql_stmt_store_result(statement->stmt); |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
265 | } |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
266 | |
1 | 267 | cleanup: |
23
a4825c3e65e9
Bugfix: memory corruption possible after reallocs. Using a static buffer instead. Will need to check for overflows on static buffer.
nrich@ii.net
parents:
21
diff
changeset
|
268 | if (bind) { |
1 | 269 | free(bind); |
23
a4825c3e65e9
Bugfix: memory corruption possible after reallocs. Using a static buffer instead. Will need to check for overflows on static buffer.
nrich@ii.net
parents:
21
diff
changeset
|
270 | } |
1 | 271 | |
25 | 272 | if (buffer) { |
273 | free(buffer); | |
274 | } | |
275 | ||
1 | 276 | if (error_message) { |
3 | 277 | lua_pushboolean(L, 0); |
10 | 278 | lua_pushfstring(L, error_message, errstr ? errstr : mysql_stmt_error(statement->stmt)); |
3 | 279 | return 2; |
1 | 280 | } |
281 | ||
282 | statement->metadata = metadata; | |
283 | ||
3 | 284 | lua_pushboolean(L, 1); |
1 | 285 | return 1; |
286 | } | |
287 | ||
11
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
288 | static int statement_fetch_impl(lua_State *L, statement_t *statement, int named_columns) { |
45
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
289 | int column_count, fetch_result_ok; |
1 | 290 | MYSQL_BIND *bind = NULL; |
45
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
291 | unsigned long *real_length = NULL; |
1 | 292 | const char *error_message = NULL; |
293 | ||
294 | if (!statement->stmt) { | |
4 | 295 | luaL_error(L, DBI_ERR_FETCH_INVALID); |
3 | 296 | return 0; |
297 | } | |
298 | ||
299 | if (!statement->metadata) { | |
4 | 300 | luaL_error(L, DBI_ERR_FETCH_NO_EXECUTE); |
3 | 301 | return 0; |
1 | 302 | } |
303 | ||
304 | column_count = mysql_num_fields(statement->metadata); | |
305 | ||
306 | if (column_count > 0) { | |
307 | int i; | |
308 | MYSQL_FIELD *fields; | |
309 | ||
45
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
310 | real_length = calloc(column_count, sizeof(unsigned long)); |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
311 | |
1 | 312 | bind = malloc(sizeof(MYSQL_BIND) * column_count); |
313 | memset(bind, 0, sizeof(MYSQL_BIND) * column_count); | |
314 | ||
315 | fields = mysql_fetch_fields(statement->metadata); | |
316 | ||
317 | for (i = 0; i < column_count; i++) { | |
33 | 318 | unsigned int length = mysql_buffer_size(&fields[i]); |
45
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
319 | if (length > sizeof(MYSQL_TIME)) { |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
320 | bind[i].buffer = NULL; |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
321 | bind[i].buffer_length = 0; |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
322 | } else { |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
323 | char *buffer = (char *)malloc(length); |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
324 | memset(buffer, 0, length); |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
325 | |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
326 | bind[i].buffer = buffer; |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
327 | bind[i].buffer_length = length; |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
328 | } |
1 | 329 | |
330 | bind[i].buffer_type = fields[i].type; | |
45
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
331 | bind[i].length = &real_length[i]; |
1 | 332 | } |
333 | ||
334 | if (mysql_stmt_bind_result(statement->stmt, bind)) { | |
4 | 335 | error_message = DBI_ERR_BINDING_RESULTS; |
1 | 336 | goto cleanup; |
337 | } | |
338 | ||
45
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
339 | fetch_result_ok = mysql_stmt_fetch(statement->stmt); |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
340 | if (fetch_result_ok == 0 || fetch_result_ok == MYSQL_DATA_TRUNCATED) { |
1 | 341 | int d = 1; |
342 | ||
343 | lua_newtable(L); | |
344 | for (i = 0; i < column_count; i++) { | |
345 | lua_push_type_t lua_push = mysql_to_lua_push(fields[i].type); | |
346 | const char *name = fields[i].name; | |
347 | ||
45
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
348 | if (bind[i].buffer == NULL) { |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
349 | char *buffer = (char *)malloc(real_length[i]); |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
350 | memset(buffer, 0, real_length[i]); |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
351 | |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
352 | bind[i].buffer = buffer; |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
353 | bind[i].buffer_length = real_length[i]; |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
354 | mysql_stmt_fetch_column(statement->stmt, bind, i, 0); |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
355 | } |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
356 | |
1 | 357 | if (lua_push == LUA_PUSH_NIL) { |
358 | if (named_columns) { | |
359 | LUA_PUSH_ATTRIB_NIL(name); | |
360 | } else { | |
361 | LUA_PUSH_ARRAY_NIL(d); | |
362 | } | |
363 | } else if (lua_push == LUA_PUSH_INTEGER) { | |
36 | 364 | if (fields[i].type == MYSQL_TYPE_YEAR || fields[i].type == MYSQL_TYPE_SHORT) { |
365 | if (named_columns) { | |
366 | LUA_PUSH_ATTRIB_INT(name, *(short *)(bind[i].buffer)); | |
367 | } else { | |
368 | LUA_PUSH_ARRAY_INT(d, *(short *)(bind[i].buffer)); | |
369 | } | |
370 | } else if (fields[i].type == MYSQL_TYPE_TINY) { | |
371 | if (named_columns) { | |
372 | LUA_PUSH_ATTRIB_INT(name, (int)*(char *)(bind[i].buffer)); | |
373 | } else { | |
374 | LUA_PUSH_ARRAY_INT(d, (int)*(char *)(bind[i].buffer)); | |
375 | } | |
1 | 376 | } else { |
36 | 377 | if (named_columns) { |
378 | LUA_PUSH_ATTRIB_INT(name, *(int *)(bind[i].buffer)); | |
379 | } else { | |
380 | LUA_PUSH_ARRAY_INT(d, *(int *)(bind[i].buffer)); | |
381 | } | |
1 | 382 | } |
383 | } else if (lua_push == LUA_PUSH_NUMBER) { | |
384 | if (named_columns) { | |
385 | LUA_PUSH_ATTRIB_FLOAT(name, *(double *)(bind[i].buffer)); | |
386 | } else { | |
387 | LUA_PUSH_ARRAY_FLOAT(d, *(double *)(bind[i].buffer)); | |
388 | } | |
389 | } else if (lua_push == LUA_PUSH_STRING) { | |
33 | 390 | |
391 | if (fields[i].type == MYSQL_TYPE_TIMESTAMP || fields[i].type == MYSQL_TYPE_DATETIME) { | |
392 | char str[20]; | |
393 | struct st_mysql_time *t = bind[i].buffer; | |
394 | ||
395 | snprintf(str, 20, "%d-%02d-%02d %02d:%02d:%02d", t->year, t->month, t->day, t->hour, t->minute, t->second); | |
396 | ||
397 | if (named_columns) { | |
398 | LUA_PUSH_ATTRIB_STRING(name, str); | |
399 | } else { | |
400 | LUA_PUSH_ARRAY_STRING(d, str); | |
401 | } | |
402 | } else if (fields[i].type == MYSQL_TYPE_TIME) { | |
403 | char str[9]; | |
404 | struct st_mysql_time *t = bind[i].buffer; | |
405 | ||
406 | snprintf(str, 9, "%02d:%02d:%02d", t->hour, t->minute, t->second); | |
407 | ||
408 | if (named_columns) { | |
409 | LUA_PUSH_ATTRIB_STRING(name, str); | |
410 | } else { | |
411 | LUA_PUSH_ARRAY_STRING(d, str); | |
412 | } | |
413 | } else if (fields[i].type == MYSQL_TYPE_DATE) { | |
414 | char str[20]; | |
415 | struct st_mysql_time *t = bind[i].buffer; | |
416 | ||
417 | snprintf(str, 11, "%d-%02d-%02d", t->year, t->month, t->day); | |
418 | ||
419 | if (named_columns) { | |
420 | LUA_PUSH_ATTRIB_STRING(name, str); | |
421 | } else { | |
422 | LUA_PUSH_ARRAY_STRING(d, str); | |
423 | } | |
424 | ||
1 | 425 | } else { |
33 | 426 | if (named_columns) { |
427 | LUA_PUSH_ATTRIB_STRING(name, bind[i].buffer); | |
428 | } else { | |
429 | LUA_PUSH_ARRAY_STRING(d, bind[i].buffer); | |
430 | } | |
1 | 431 | } |
432 | } else if (lua_push == LUA_PUSH_BOOLEAN) { | |
433 | if (named_columns) { | |
434 | LUA_PUSH_ATTRIB_BOOL(name, *(int *)(bind[i].buffer)); | |
435 | } else { | |
436 | LUA_PUSH_ARRAY_BOOL(d, *(int *)(bind[i].buffer)); | |
437 | } | |
438 | } else { | |
4 | 439 | luaL_error(L, DBI_ERR_UNKNOWN_PUSH); |
1 | 440 | } |
441 | } | |
442 | } else { | |
443 | lua_pushnil(L); | |
444 | } | |
445 | } | |
446 | ||
447 | cleanup: | |
45
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
448 | free(real_length); |
7c968f66bccd
MySQL: Avoid allocating the full column size to receive results, for variable-length types check result size before allocation (thanks Florob)
Matthew Wild <mwild1@gmail.com>
parents:
36
diff
changeset
|
449 | |
1 | 450 | if (bind) { |
451 | int i; | |
452 | ||
453 | for (i = 0; i < column_count; i++) { | |
454 | free(bind[i].buffer); | |
455 | } | |
456 | ||
457 | free(bind); | |
458 | } | |
459 | ||
460 | if (error_message) { | |
461 | luaL_error(L, error_message, mysql_stmt_error(statement->stmt)); | |
462 | return 0; | |
463 | } | |
464 | ||
465 | return 1; | |
466 | } | |
467 | ||
11
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
468 | static int next_iterator(lua_State *L) { |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
469 | statement_t *statement = (statement_t *)luaL_checkudata(L, lua_upvalueindex(1), DBD_MYSQL_STATEMENT); |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
470 | int named_columns = lua_toboolean(L, lua_upvalueindex(2)); |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
471 | |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
472 | return statement_fetch_impl(L, statement, named_columns); |
1 | 473 | } |
474 | ||
2 | 475 | /* |
12
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
476 | * table = statement:fetch(named_indexes) |
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
477 | */ |
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
478 | static int statement_fetch(lua_State *L) { |
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
479 | statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); |
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
480 | int named_columns = lua_toboolean(L, 2); |
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
481 | |
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
482 | return statement_fetch_impl(L, statement, named_columns); |
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
483 | } |
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
484 | |
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
485 | /* |
21
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
486 | * num_rows = statement:rowcount() |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
487 | */ |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
488 | static int statement_rowcount(lua_State *L) { |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
489 | statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
490 | |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
491 | if (!statement->stmt) { |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
492 | luaL_error(L, DBI_ERR_INVALID_STATEMENT); |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
493 | } |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
494 | |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
495 | lua_pushinteger(L, mysql_stmt_num_rows(statement->stmt)); |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
496 | |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
497 | return 1; |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
498 | } |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
499 | |
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
500 | /* |
12
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
501 | * iterfunc = statement:rows(named_indexes) |
2 | 502 | */ |
12
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
503 | static int statement_rows(lua_State *L) { |
11
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
504 | if (lua_gettop(L) == 1) { |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
505 | lua_pushvalue(L, 1); |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
506 | lua_pushboolean(L, 0); |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
507 | } else { |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
508 | lua_pushvalue(L, 1); |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
509 | lua_pushboolean(L, lua_toboolean(L, 2)); |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
510 | } |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
511 | |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
512 | lua_pushcclosure(L, next_iterator, 2); |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
513 | return 1; |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
514 | } |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
515 | |
b3e05e361f46
Bugfix: PSQL array returns were not being indexed properly.
nrich@ii.net
parents:
10
diff
changeset
|
516 | /* |
2 | 517 | * __gc |
518 | */ | |
1 | 519 | static int statement_gc(lua_State *L) { |
520 | /* always free the handle */ | |
521 | statement_close(L); | |
522 | ||
523 | return 0; | |
524 | } | |
525 | ||
32
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
526 | /* |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
527 | * __tostring |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
528 | */ |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
529 | static int statement_tostring(lua_State *L) { |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
530 | statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
531 | |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
532 | lua_pushfstring(L, "%s: %p", DBD_MYSQL_STATEMENT, statement); |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
533 | |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
534 | return 1; |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
535 | } |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
536 | |
1 | 537 | int dbd_mysql_statement_create(lua_State *L, connection_t *conn, const char *sql_query) { |
538 | unsigned long sql_len = strlen(sql_query); | |
539 | ||
540 | statement_t *statement = NULL; | |
541 | ||
542 | MYSQL_STMT *stmt = mysql_stmt_init(conn->mysql); | |
543 | ||
544 | if (!stmt) { | |
3 | 545 | lua_pushnil(L); |
4 | 546 | lua_pushfstring(L, DBI_ERR_ALLOC_STATEMENT, mysql_error(conn->mysql)); |
3 | 547 | return 2; |
1 | 548 | } |
549 | ||
550 | if (mysql_stmt_prepare(stmt, sql_query, sql_len)) { | |
3 | 551 | lua_pushnil(L); |
4 | 552 | lua_pushfstring(L, DBI_ERR_PREP_STATEMENT, mysql_stmt_error(stmt)); |
3 | 553 | return 2; |
1 | 554 | } |
555 | ||
556 | statement = (statement_t *)lua_newuserdata(L, sizeof(statement_t)); | |
557 | statement->mysql = conn->mysql; | |
558 | statement->stmt = stmt; | |
559 | statement->metadata = NULL; | |
560 | ||
33 | 561 | /* |
562 | mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (my_bool*)0); | |
563 | */ | |
564 | ||
1 | 565 | luaL_getmetatable(L, DBD_MYSQL_STATEMENT); |
566 | lua_setmetatable(L, -2); | |
567 | ||
568 | return 1; | |
569 | } | |
570 | ||
571 | int dbd_mysql_statement(lua_State *L) { | |
2 | 572 | static const luaL_Reg statement_methods[] = { |
21
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
573 | {"affected", statement_affected}, |
2 | 574 | {"close", statement_close}, |
30
8599f34c139b
Add 'columns' method to statement handles to retrieve column names from a result set
nrich@ii.net
parents:
26
diff
changeset
|
575 | {"columns", statement_columns}, |
2 | 576 | {"execute", statement_execute}, |
577 | {"fetch", statement_fetch}, | |
21
7956401a0c5e
Added the statement:affected() and statement:rowcount() methods.
nrich@ii.net
parents:
14
diff
changeset
|
578 | {"rowcount", statement_rowcount}, |
12
014ba3ab3903
Renamed statement:fetch() to statement:rows(), and statement:row() to statement:fetch(). The API reads better this way.
nrich@ii.net
parents:
11
diff
changeset
|
579 | {"rows", statement_rows}, |
2 | 580 | {NULL, NULL} |
581 | }; | |
582 | ||
583 | static const luaL_Reg statement_class_methods[] = { | |
584 | {NULL, NULL} | |
585 | }; | |
586 | ||
1 | 587 | luaL_newmetatable(L, DBD_MYSQL_STATEMENT); |
588 | luaL_register(L, 0, statement_methods); | |
589 | lua_pushvalue(L,-1); | |
590 | lua_setfield(L, -2, "__index"); | |
591 | ||
592 | lua_pushcfunction(L, statement_gc); | |
593 | lua_setfield(L, -2, "__gc"); | |
594 | ||
32
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
595 | lua_pushcfunction(L, statement_tostring); |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
596 | lua_setfield(L, -2, "__tostring"); |
03ed0ca09837
Add __tostring method to connection and statement objects.
nrich@ii.net
parents:
30
diff
changeset
|
597 | |
1 | 598 | luaL_register(L, DBD_MYSQL_STATEMENT, statement_class_methods); |
599 | ||
600 | return 1; | |
601 | } |