1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2014 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andrey Hristov <andrey@mysql.com> |
16 | Ulf Wendel <uwendel@mysql.com> |
17 | Georg Richter <georg@mysql.com> |
18 +----------------------------------------------------------------------+
19 */
20
21 /* $Id$ */
22 #include "php.h"
23 #include "mysqlnd.h"
24 #include "mysqlnd_priv.h"
25 #include "mysqlnd_result.h"
26 #include "mysqlnd_wireprotocol.h"
27 #include "mysqlnd_debug.h"
28 #include "ext/standard/basic_functions.h"
29
30
31 /* {{{ php_mysqlnd_free_field_metadata */
32 static void
php_mysqlnd_free_field_metadata(MYSQLND_FIELD * meta,zend_bool persistent TSRMLS_DC)33 php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent TSRMLS_DC)
34 {
35 if (meta) {
36 if (meta->root) {
37 mnd_pefree(meta->root, persistent);
38 meta->root = NULL;
39 }
40 if (meta->def) {
41 mnd_pefree(meta->def, persistent);
42 meta->def = NULL;
43 }
44 }
45 }
46 /* }}} */
47
48
49 /* {{{ mysqlnd_handle_numeric */
50 /*
51 The following code is stolen from ZE - HANDLE_NUMERIC() macro from zend_hash.c
52 and modified for the needs of mysqlnd.
53 */
54 static zend_bool
mysqlnd_is_key_numeric(const char * key,size_t length,long * idx)55 mysqlnd_is_key_numeric(const char * key, size_t length, long *idx)
56 {
57 register const char * tmp = key;
58
59 if (*tmp=='-') {
60 tmp++;
61 }
62 if ((*tmp>='0' && *tmp<='9')) {
63 do { /* possibly a numeric index */
64 const char *end=key+length-1;
65
66 if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */
67 break;
68 }
69 while (tmp<end) {
70 if (!(*tmp>='0' && *tmp<='9')) {
71 break;
72 }
73 tmp++;
74 }
75 if (tmp==end && *tmp=='\0') { /* a numeric index */
76 if (*key=='-') {
77 *idx = strtol(key, NULL, 10);
78 if (*idx!=LONG_MIN) {
79 return TRUE;
80 }
81 } else {
82 *idx = strtol(key, NULL, 10);
83 if (*idx!=LONG_MAX) {
84 return TRUE;
85 }
86 }
87 }
88 } while (0);
89 }
90 return FALSE;
91 }
92 /* }}} */
93
94
95 #if MYSQLND_UNICODE
96 /* {{{ mysqlnd_unicode_is_key_numeric */
97 static zend_bool
mysqlnd_unicode_is_key_numeric(UChar * key,size_t length,long * idx)98 mysqlnd_unicode_is_key_numeric(UChar *key, size_t length, long *idx)
99 {
100 register UChar * tmp=key;
101
102 if (*tmp==0x2D /*'-'*/) {
103 tmp++;
104 }
105 if ((*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) { /* possibly a numeric index */
106 do {
107 UChar *end=key+length-1;
108
109 if (*tmp++==0x30 && length>2) { /* don't accept numbers with leading zeros */
110 break;
111 }
112 while (tmp<end) {
113 if (!(*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) {
114 break;
115 }
116 tmp++;
117 }
118 if (tmp==end && *tmp==0) { /* a numeric index */
119 if (*key==0x2D /*'-'*/) {
120 *idx = zend_u_strtol(key, NULL, 10);
121 if (*idx!=LONG_MIN) {
122 return TRUE;
123 }
124 } else {
125 *idx = zend_u_strtol(key, NULL, 10);
126 if (*idx!=LONG_MAX) {
127 return TRUE;
128 }
129 }
130 }
131 } while (0);
132 }
133 return FALSE;
134 }
135 /* }}} */
136 #endif
137
138
139 /* {{{ mysqlnd_res_meta::read_metadata */
140 static enum_func_status
MYSQLND_METHOD(mysqlnd_res_meta,read_metadata)141 MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn TSRMLS_DC)
142 {
143 unsigned int i = 0;
144 MYSQLND_PACKET_RES_FIELD * field_packet;
145 #if MYSQLND_UNICODE
146 UChar *ustr;
147 int ulen;
148 #endif
149
150 DBG_ENTER("mysqlnd_res_meta::read_metadata");
151
152 field_packet = conn->protocol->m.get_result_field_packet(conn->protocol, FALSE TSRMLS_CC);
153 if (!field_packet) {
154 SET_OOM_ERROR(*conn->error_info);
155 DBG_RETURN(FAIL);
156 }
157 field_packet->persistent_alloc = meta->persistent;
158 for (;i < meta->field_count; i++) {
159 long idx;
160
161 if (meta->fields[i].root) {
162 /* We re-read metadata for PS */
163 mnd_pefree(meta->fields[i].root, meta->persistent);
164 meta->fields[i].root = NULL;
165 }
166
167 field_packet->metadata = &(meta->fields[i]);
168 if (FAIL == PACKET_READ(field_packet, conn)) {
169 PACKET_FREE(field_packet);
170 DBG_RETURN(FAIL);
171 }
172 if (field_packet->error_info.error_no) {
173 COPY_CLIENT_ERROR(*conn->error_info, field_packet->error_info);
174 /* Return back from CONN_QUERY_SENT */
175 PACKET_FREE(field_packet);
176 DBG_RETURN(FAIL);
177 }
178
179 if (field_packet->stupid_list_fields_eof == TRUE) {
180 meta->field_count = i;
181 break;
182 }
183
184 if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
185 DBG_ERR_FMT("Unknown type %u sent by the server. Please send a report to the developers",
186 meta->fields[i].type);
187 php_error_docref(NULL TSRMLS_CC, E_WARNING,
188 "Unknown type %u sent by the server. "
189 "Please send a report to the developers",
190 meta->fields[i].type);
191 PACKET_FREE(field_packet);
192 DBG_RETURN(FAIL);
193 }
194 if (meta->fields[i].type == MYSQL_TYPE_BIT) {
195 size_t field_len;
196 DBG_INF("BIT");
197 ++meta->bit_fields_count;
198 /* .length is in bits */
199 field_len = meta->fields[i].length / 8;
200 /*
201 If there is rest, add one byte :
202 8 bits = 1 byte but 9 bits = 2 bytes
203 */
204 if (meta->fields[i].length % 8) {
205 ++field_len;
206 }
207 switch (field_len) {
208 case 8:
209 case 7:
210 case 6:
211 case 5:
212 meta->bit_fields_total_len += 20;/* 21 digis, no sign*/
213 break;
214 case 4:
215 meta->bit_fields_total_len += 10;/* 2 000 000 000*/
216 break;
217 case 3:
218 meta->bit_fields_total_len += 8;/* 12 000 000*/
219 break;
220 case 2:
221 meta->bit_fields_total_len += 5;/* 32 500 */
222 break;
223 case 1:
224 meta->bit_fields_total_len += 3;/* 120 */
225 break;
226 }
227 }
228
229 #if MYSQLND_UNICODE
230 zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen,
231 meta->fields[i].name,
232 meta->fields[i].name_length TSRMLS_CC);
233 if ((meta->zend_hash_keys[i].is_numeric =
234 mysqlnd_unicode_is_key_numeric(ustr, ulen + 1, &idx)))
235 {
236 meta->zend_hash_keys[i].key = idx;
237 mnd_efree(ustr);
238 } else {
239 meta->zend_hash_keys[i].ustr.u = ustr;
240 meta->zend_hash_keys[i].ulen = ulen;
241 meta->zend_hash_keys[i].key = zend_u_get_hash_value(IS_UNICODE, ZSTR(ustr), ulen + 1);
242 }
243 #else
244 /* For BC we have to check whether the key is numeric and use it like this */
245 if ((meta->zend_hash_keys[i].is_numeric =
246 mysqlnd_is_key_numeric(field_packet->metadata->name,
247 field_packet->metadata->name_length + 1,
248 &idx)))
249 {
250 meta->zend_hash_keys[i].key = idx;
251 } else {
252 meta->zend_hash_keys[i].key =
253 zend_get_hash_value(field_packet->metadata->name,
254 field_packet->metadata->name_length + 1);
255 }
256 #endif
257 }
258 PACKET_FREE(field_packet);
259
260 DBG_RETURN(PASS);
261 }
262 /* }}} */
263
264
265 /* {{{ mysqlnd_res_meta::free */
266 static void
MYSQLND_METHOD(mysqlnd_res_meta,free)267 MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA * meta TSRMLS_DC)
268 {
269 int i;
270 MYSQLND_FIELD *fields;
271 DBG_ENTER("mysqlnd_res_meta::free");
272 DBG_INF_FMT("persistent=%u", meta->persistent);
273
274 if ((fields = meta->fields)) {
275 DBG_INF("Freeing fields metadata");
276 i = meta->field_count;
277 while (i--) {
278 php_mysqlnd_free_field_metadata(fields++, meta->persistent TSRMLS_CC);
279 }
280 mnd_pefree(meta->fields, meta->persistent);
281 meta->fields = NULL;
282 }
283
284 if (meta->zend_hash_keys) {
285 DBG_INF("Freeing zend_hash_keys");
286 #if MYSQLND_UNICODE
287 if (UG(unicode)) {
288 for (i = 0; i < meta->field_count; i++) {
289 if (meta->zend_hash_keys[i].ustr.v) {
290 mnd_pefree(meta->zend_hash_keys[i].ustr.v, meta->persistent);
291 }
292 }
293 }
294 #endif
295 mnd_pefree(meta->zend_hash_keys, meta->persistent);
296 meta->zend_hash_keys = NULL;
297 }
298 DBG_INF("Freeing metadata structure");
299 mnd_pefree(meta, meta->persistent);
300
301 DBG_VOID_RETURN;
302 }
303 /* }}} */
304
305
306 /* {{{ mysqlnd_res::clone_metadata */
307 static MYSQLND_RES_METADATA *
MYSQLND_METHOD(mysqlnd_res_meta,clone_metadata)308 MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC)
309 {
310 unsigned int i;
311 /* +1 is to have empty marker at the end */
312 MYSQLND_RES_METADATA * new_meta = NULL;
313 MYSQLND_FIELD * new_fields;
314 MYSQLND_FIELD * orig_fields = meta->fields;
315 size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key);
316
317 DBG_ENTER("mysqlnd_res_meta::clone_metadata");
318 DBG_INF_FMT("persistent=%u", persistent);
319
320 new_meta = mnd_pecalloc(1, sizeof(MYSQLND_RES_METADATA), persistent);
321 if (!new_meta) {
322 goto oom;
323 }
324 new_meta->persistent = persistent;
325 new_meta->m = meta->m;
326
327 new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent);
328 if (!new_fields) {
329 goto oom;
330 }
331
332 new_meta->zend_hash_keys = mnd_pemalloc(len, persistent);
333 if (!new_meta->zend_hash_keys) {
334 goto oom;
335 }
336 memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len);
337
338 /*
339 This will copy also the strings and the root, which we will have
340 to adjust in the loop
341 */
342 memcpy(new_fields, orig_fields, (meta->field_count) * sizeof(MYSQLND_FIELD));
343 for (i = 0; i < meta->field_count; i++) {
344 /* First copy the root, then field by field adjust the pointers */
345 new_fields[i].root = mnd_pemalloc(orig_fields[i].root_len, persistent);
346 if (!new_fields[i].root) {
347 goto oom;
348 }
349 memcpy(new_fields[i].root, orig_fields[i].root, new_fields[i].root_len);
350
351 if (orig_fields[i].name && orig_fields[i].name != mysqlnd_empty_string) {
352 new_fields[i].name = new_fields[i].root +
353 (orig_fields[i].name - orig_fields[i].root);
354 }
355 if (orig_fields[i].org_name && orig_fields[i].org_name != mysqlnd_empty_string) {
356 new_fields[i].org_name = new_fields[i].root +
357 (orig_fields[i].org_name - orig_fields[i].root);
358 }
359 if (orig_fields[i].table && orig_fields[i].table != mysqlnd_empty_string) {
360 new_fields[i].table = new_fields[i].root +
361 (orig_fields[i].table - orig_fields[i].root);
362 }
363 if (orig_fields[i].org_table && orig_fields[i].org_table != mysqlnd_empty_string) {
364 new_fields[i].org_table = new_fields[i].root +
365 (orig_fields[i].org_table - orig_fields[i].root);
366 }
367 if (orig_fields[i].db && orig_fields[i].db != mysqlnd_empty_string) {
368 new_fields[i].db = new_fields[i].root + (orig_fields[i].db - orig_fields[i].root);
369 }
370 if (orig_fields[i].catalog && orig_fields[i].catalog != mysqlnd_empty_string) {
371 new_fields[i].catalog = new_fields[i].root + (orig_fields[i].catalog - orig_fields[i].root);
372 }
373 /* def is not on the root, if allocated at all */
374 if (orig_fields[i].def) {
375 new_fields[i].def = mnd_pemalloc(orig_fields[i].def_length + 1, persistent);
376 if (!new_fields[i].def) {
377 goto oom;
378 }
379 /* copy the trailing \0 too */
380 memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1);
381 }
382 #if MYSQLND_UNICODE
383 if (new_meta->zend_hash_keys[i].ustr.u) {
384 new_meta->zend_hash_keys[i].ustr.u =
385 eustrndup(new_meta->zend_hash_keys[i].ustr.u, new_meta->zend_hash_keys[i].ulen);
386 if (!new_meta->zend_hash_keys[i].ustr.u) {
387 goto oom;
388 }
389 }
390 #endif
391 }
392 new_meta->current_field = 0;
393 new_meta->field_count = meta->field_count;
394
395 new_meta->fields = new_fields;
396
397 DBG_RETURN(new_meta);
398 oom:
399 if (new_meta) {
400 new_meta->m->free_metadata(new_meta TSRMLS_CC);
401 new_meta = NULL;
402 }
403 DBG_RETURN(NULL);
404 }
405 /* }}} */
406
407 /* {{{ mysqlnd_res_meta::fetch_field */
408 static const MYSQLND_FIELD *
MYSQLND_METHOD(mysqlnd_res_meta,fetch_field)409 MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
410 {
411 DBG_ENTER("mysqlnd_res_meta::fetch_field");
412 if (meta->current_field >= meta->field_count) {
413 DBG_INF("no more fields");
414 DBG_RETURN(NULL);
415 }
416 DBG_INF_FMT("name=%s max_length=%u",
417 meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
418 meta->fields[meta->current_field].max_length);
419 DBG_RETURN(&meta->fields[meta->current_field++]);
420 }
421 /* }}} */
422
423
424 /* {{{ mysqlnd_res_meta::fetch_field_direct */
425 static const MYSQLND_FIELD *
MYSQLND_METHOD(mysqlnd_res_meta,fetch_field_direct)426 MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
427 {
428 DBG_ENTER("mysqlnd_res_meta::fetch_field_direct");
429 DBG_INF_FMT("fieldnr=%u", fieldnr);
430 DBG_INF_FMT("name=%s max_length=%u",
431 meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
432 meta->fields[meta->current_field].max_length);
433 DBG_RETURN(&meta->fields[fieldnr]);
434 }
435 /* }}} */
436
437
438 /* {{{ mysqlnd_res_meta::fetch_fields */
439 static const MYSQLND_FIELD *
MYSQLND_METHOD(mysqlnd_res_meta,fetch_fields)440 MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
441 {
442 DBG_ENTER("mysqlnd_res_meta::fetch_fields");
443 DBG_RETURN(meta->fields);
444 }
445 /* }}} */
446
447
448 /* {{{ mysqlnd_res_meta::field_tell */
449 static MYSQLND_FIELD_OFFSET
MYSQLND_METHOD(mysqlnd_res_meta,field_tell)450 MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const meta TSRMLS_DC)
451 {
452 return meta->current_field;
453 }
454 /* }}} */
455
456
457 static
458 MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta)
459 MYSQLND_METHOD(mysqlnd_res_meta, fetch_field),
460 MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct),
461 MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields),
462 MYSQLND_METHOD(mysqlnd_res_meta, field_tell),
463 MYSQLND_METHOD(mysqlnd_res_meta, read_metadata),
464 MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata),
465 MYSQLND_METHOD(mysqlnd_res_meta, free),
466 MYSQLND_CLASS_METHODS_END;
467
468
469 /* {{{ mysqlnd_result_meta_init */
470 PHPAPI MYSQLND_RES_METADATA *
mysqlnd_result_meta_init(unsigned int field_count,zend_bool persistent TSRMLS_DC)471 mysqlnd_result_meta_init(unsigned int field_count, zend_bool persistent TSRMLS_DC)
472 {
473 size_t alloc_size = sizeof(MYSQLND_RES_METADATA) + mysqlnd_plugin_count() * sizeof(void *);
474 MYSQLND_RES_METADATA *ret = mnd_pecalloc(1, alloc_size, persistent);
475 DBG_ENTER("mysqlnd_result_meta_init");
476 DBG_INF_FMT("persistent=%u", persistent);
477
478 do {
479 if (!ret) {
480 break;
481 }
482 ret->m = & mysqlnd_mysqlnd_res_meta_methods;
483
484 ret->persistent = persistent;
485 ret->field_count = field_count;
486 /* +1 is to have empty marker at the end */
487 ret->fields = mnd_pecalloc(field_count + 1, sizeof(MYSQLND_FIELD), ret->persistent);
488 ret->zend_hash_keys = mnd_pecalloc(field_count, sizeof(struct mysqlnd_field_hash_key), ret->persistent);
489 if (!ret->fields || !ret->zend_hash_keys) {
490 break;
491 }
492 DBG_INF_FMT("meta=%p", ret);
493 DBG_RETURN(ret);
494 } while (0);
495 if (ret) {
496 ret->m->free_metadata(ret TSRMLS_CC);
497 }
498 DBG_RETURN(NULL);
499 }
500 /* }}} */
501
502
503 /* {{{ mysqlnd_res_meta_get_methods */
504 PHPAPI struct st_mysqlnd_res_meta_methods *
mysqlnd_result_metadata_get_methods()505 mysqlnd_result_metadata_get_methods()
506 {
507 return &mysqlnd_mysqlnd_res_meta_methods;
508 }
509 /* }}} */
510
511
512 /* {{{ _mysqlnd_plugin_get_plugin_result_metadata_data */
513 PHPAPI void **
_mysqlnd_plugin_get_plugin_result_metadata_data(const MYSQLND_RES_METADATA * meta,unsigned int plugin_id TSRMLS_DC)514 _mysqlnd_plugin_get_plugin_result_metadata_data(const MYSQLND_RES_METADATA * meta, unsigned int plugin_id TSRMLS_DC)
515 {
516 DBG_ENTER("_mysqlnd_plugin_get_plugin_result_metadata_data");
517 DBG_INF_FMT("plugin_id=%u", plugin_id);
518 if (!meta || plugin_id >= mysqlnd_plugin_count()) {
519 return NULL;
520 }
521 DBG_RETURN((void *)((char *)meta + sizeof(MYSQLND_RES_METADATA) + plugin_id * sizeof(void *)));
522 }
523 /* }}} */
524
525 /*
526 * Local variables:
527 * tab-width: 4
528 * c-basic-offset: 4
529 * End:
530 * vim600: noet sw=4 ts=4 fdm=marker
531 * vim<600: noet sw=4 ts=4
532 */
533