1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 2006-2009 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: Georg Richter <georg@mysql.com> |
16 : | Andrey Hristov <andrey@mysql.com> |
17 : | Ulf Wendel <uwendel@mysql.com> |
18 : +----------------------------------------------------------------------+
19 : */
20 :
21 : /* $Id: mysqlnd_result_meta.c 288202 2009-09-09 17:03:03Z uw $ */
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
33 : void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent TSRMLS_DC)
34 162055 : {
35 162055 : if (meta) {
36 162055 : if (meta->root) {
37 25983 : mnd_pefree(meta->root, persistent);
38 25983 : meta->root = NULL;
39 : }
40 162055 : if (meta->def) {
41 3 : mnd_pefree(meta->def, persistent);
42 3 : meta->def = NULL;
43 : }
44 : }
45 162055 : }
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
55 : zend_bool mysqlnd_is_key_numeric(char *key, size_t length, long *idx)
56 22688 : {
57 22688 : register char *tmp=key;
58 :
59 22688 : if (*tmp=='-') {
60 5 : tmp++;
61 : }
62 22688 : if ((*tmp>='0' && *tmp<='9')) {
63 : do { /* possibly a numeric index */
64 63 : char *end=key+length-1;
65 :
66 63 : if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */
67 2 : break;
68 : }
69 146 : while (tmp<end) {
70 28 : if (!(*tmp>='0' && *tmp<='9')) {
71 : break;
72 : }
73 24 : tmp++;
74 : }
75 61 : if (tmp==end && *tmp=='\0') { /* a numeric index */
76 57 : if (*key=='-') {
77 4 : *idx = strtol(key, NULL, 10);
78 4 : if (*idx!=LONG_MIN) {
79 4 : return TRUE;
80 : }
81 : } else {
82 53 : *idx = strtol(key, NULL, 10);
83 53 : if (*idx!=LONG_MAX) {
84 53 : return TRUE;
85 : }
86 : }
87 : }
88 : } while (0);
89 : }
90 22631 : return FALSE;
91 : }
92 : /* }}} */
93 :
94 :
95 : #if PHP_MAJOR_VERSION >= 6
96 : /* {{{ mysqlnd_unicode_is_key_numeric */
97 : static
98 : zend_bool 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
141 : MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const meta,
142 : MYSQLND *conn TSRMLS_DC)
143 6784 : {
144 6784 : unsigned int i = 0;
145 : php_mysql_packet_res_field field_packet;
146 : #if PHP_MAJOR_VERSION >= 6
147 : UChar *ustr;
148 : int ulen;
149 : #endif
150 :
151 6784 : DBG_ENTER("mysqlnd_res_meta::read_metadata");
152 :
153 6784 : PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET);
154 29472 : for (;i < meta->field_count; i++) {
155 : long idx;
156 :
157 22693 : if (meta->fields[i].root) {
158 : /* We re-read metadata for PS */
159 0 : mnd_efree(meta->fields[i].root);
160 0 : meta->fields[i].root = NULL;
161 : }
162 :
163 22693 : field_packet.metadata = &(meta->fields[i]);
164 22693 : if (FAIL == PACKET_READ_ALLOCA(field_packet, conn)) {
165 1 : PACKET_FREE_ALLOCA(field_packet);
166 1 : DBG_RETURN(FAIL);
167 : }
168 22692 : if (field_packet.error_info.error_no) {
169 1 : conn->error_info = field_packet.error_info;
170 : /* Return back from CONN_QUERY_SENT */
171 1 : PACKET_FREE_ALLOCA(field_packet);
172 1 : DBG_RETURN(FAIL);
173 : }
174 :
175 22691 : if (field_packet.stupid_list_fields_eof == TRUE) {
176 3 : meta->field_count = i;
177 3 : break;
178 : }
179 :
180 22688 : if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
181 0 : DBG_ERR_FMT("Unknown type %d sent by the server. Please send a report to the developers",
182 : meta->fields[i].type);
183 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING,
184 : "Unknown type %d sent by the server. "
185 : "Please send a report to the developers",
186 : meta->fields[i].type);
187 0 : PACKET_FREE_ALLOCA(field_packet);
188 0 : DBG_RETURN(FAIL);
189 : }
190 22688 : if (meta->fields[i].type == MYSQL_TYPE_BIT) {
191 : size_t field_len;
192 5735 : DBG_INF("BIT");
193 5735 : ++meta->bit_fields_count;
194 : /* .length is in bits */
195 5735 : field_len = meta->fields[i].length / 8;
196 : /*
197 : If there is rest, add one byte :
198 : 8 bits = 1 byte but 9 bits = 2 bytes
199 : */
200 5735 : if (meta->fields[i].length % 8) {
201 5115 : ++field_len;
202 : }
203 5735 : switch (field_len) {
204 : case 8:
205 : case 7:
206 : case 6:
207 : case 5:
208 1400 : meta->bit_fields_total_len += 20;/* 21 digis, no sign*/
209 1400 : break;
210 : case 4:
211 1080 : meta->bit_fields_total_len += 10;/* 2 000 000 000*/
212 1080 : break;
213 : case 3:
214 1120 : meta->bit_fields_total_len += 8;/* 12 000 000*/
215 1120 : break;
216 : case 2:
217 1120 : meta->bit_fields_total_len += 5;/* 32 500 */
218 1120 : break;
219 : case 1:
220 1015 : meta->bit_fields_total_len += 3;/* 120 */
221 : break;
222 : }
223 :
224 : }
225 :
226 : #if PHP_MAJOR_VERSION >= 6
227 : zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen,
228 : meta->fields[i].name,
229 : meta->fields[i].name_length TSRMLS_CC);
230 : if ((meta->zend_hash_keys[i].is_numeric =
231 : mysqlnd_unicode_is_key_numeric(ustr, ulen + 1, &idx)))
232 : {
233 : meta->zend_hash_keys[i].key = idx;
234 : mnd_efree(ustr);
235 : } else {
236 : meta->zend_hash_keys[i].ustr.u = ustr;
237 : meta->zend_hash_keys[i].ulen = ulen;
238 : meta->zend_hash_keys[i].key = zend_u_get_hash_value(IS_UNICODE, ZSTR(ustr), ulen + 1);
239 : }
240 : #else
241 : /* For BC we have to check whether the key is numeric and use it like this */
242 22688 : if ((meta->zend_hash_keys[i].is_numeric =
243 : mysqlnd_is_key_numeric(field_packet.metadata->name,
244 : field_packet.metadata->name_length + 1,
245 : &idx)))
246 : {
247 57 : meta->zend_hash_keys[i].key = idx;
248 : } else {
249 22631 : meta->zend_hash_keys[i].key =
250 : zend_get_hash_value(field_packet.metadata->name,
251 : field_packet.metadata->name_length + 1);
252 : }
253 : #endif
254 : }
255 6782 : PACKET_FREE_ALLOCA(field_packet);
256 :
257 6782 : DBG_RETURN(PASS);
258 : }
259 : /* }}} */
260 :
261 :
262 : /* {{{ mysqlnd_res_meta::free */
263 : static void
264 : MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC)
265 7946 : {
266 : int i;
267 : MYSQLND_FIELD *fields;
268 :
269 7946 : DBG_ENTER("mysqlnd_res_meta::free");
270 7946 : DBG_INF_FMT("persistent=%d", persistent);
271 :
272 7946 : if ((fields = meta->fields)) {
273 7946 : DBG_INF("Freeing fields metadata");
274 7946 : i = meta->field_count;
275 177947 : while (i--) {
276 162055 : php_mysqlnd_free_field_metadata(fields++, persistent TSRMLS_CC);
277 : }
278 7946 : mnd_pefree(meta->fields, persistent);
279 7946 : meta->fields = NULL;
280 : }
281 :
282 7946 : if (meta->zend_hash_keys) {
283 7946 : DBG_INF("Freeing zend_hash_keys");
284 : #if PHP_MAJOR_VERSION >= 6
285 : if (UG(unicode)) {
286 : for (i = 0; i < meta->field_count; i++) {
287 : if (meta->zend_hash_keys[i].ustr.v) {
288 : mnd_pefree(meta->zend_hash_keys[i].ustr.v, persistent);
289 : }
290 : }
291 : }
292 : #endif
293 7946 : mnd_pefree(meta->zend_hash_keys, persistent);
294 7946 : meta->zend_hash_keys = NULL;
295 : }
296 7946 : DBG_INF("Freeing metadata structure");
297 7946 : mnd_pefree(meta, persistent);
298 :
299 : DBG_VOID_RETURN;
300 : }
301 : /* }}} */
302 :
303 :
304 : /* {{{ mysqlnd_res::clone_metadata */
305 : static MYSQLND_RES_METADATA *
306 : MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta,
307 : zend_bool persistent TSRMLS_DC)
308 1161 : {
309 : unsigned int i;
310 : /* +1 is to have empty marker at the end */
311 1161 : MYSQLND_RES_METADATA *new_meta = mnd_pemalloc(sizeof(MYSQLND_RES_METADATA), persistent);
312 1161 : MYSQLND_FIELD *new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent);
313 1161 : MYSQLND_FIELD *orig_fields = meta->fields;
314 1161 : size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key);
315 :
316 1161 : DBG_ENTER("mysqlnd_res_meta::clone_metadata");
317 1161 : DBG_INF_FMT("persistent=%d", persistent);
318 :
319 1161 : new_meta->zend_hash_keys = mnd_pemalloc(len, persistent);
320 1161 : memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len);
321 1161 : new_meta->m = meta->m;
322 :
323 : /*
324 : This will copy also the strings and the root, which we will have
325 : to adjust in the loop
326 : */
327 1161 : memcpy(new_fields, orig_fields, (meta->field_count) * sizeof(MYSQLND_FIELD));
328 4456 : for (i = 0; i < meta->field_count; i++) {
329 : /* First copy the root, then field by field adjust the pointers */
330 3295 : new_fields[i].root = mnd_pemalloc(orig_fields[i].root_len, persistent);
331 3295 : memcpy(new_fields[i].root, orig_fields[i].root, new_fields[i].root_len);
332 :
333 3295 : if (orig_fields[i].name && orig_fields[i].name != mysqlnd_empty_string) {
334 3295 : new_fields[i].name = new_fields[i].root +
335 : (orig_fields[i].name - orig_fields[i].root);
336 : }
337 3295 : if (orig_fields[i].org_name && orig_fields[i].org_name != mysqlnd_empty_string) {
338 2713 : new_fields[i].org_name = new_fields[i].root +
339 : (orig_fields[i].org_name - orig_fields[i].root);
340 : }
341 3295 : if (orig_fields[i].table && orig_fields[i].table != mysqlnd_empty_string) {
342 2705 : new_fields[i].table = new_fields[i].root +
343 : (orig_fields[i].table - orig_fields[i].root);
344 : }
345 3295 : if (orig_fields[i].org_table && orig_fields[i].org_table != mysqlnd_empty_string) {
346 2689 : new_fields[i].org_table = new_fields[i].root +
347 : (orig_fields[i].org_table - orig_fields[i].root);
348 : }
349 3295 : if (orig_fields[i].db && orig_fields[i].db != mysqlnd_empty_string) {
350 2689 : new_fields[i].db = new_fields[i].root + (orig_fields[i].db - orig_fields[i].root);
351 : }
352 3295 : if (orig_fields[i].catalog && orig_fields[i].catalog != mysqlnd_empty_string) {
353 3295 : new_fields[i].catalog = new_fields[i].root + (orig_fields[i].catalog - orig_fields[i].root);
354 : }
355 : /* def is not on the root, if allocated at all */
356 3295 : if (orig_fields[i].def) {
357 0 : new_fields[i].def = mnd_pemalloc(orig_fields[i].def_length + 1, persistent);
358 : /* copy the trailing \0 too */
359 0 : memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1);
360 : }
361 : #if PHP_MAJOR_VERSION >= 6
362 : if (new_meta->zend_hash_keys[i].ustr.u) {
363 : new_meta->zend_hash_keys[i].ustr.u =
364 : eustrndup(new_meta->zend_hash_keys[i].ustr.u, new_meta->zend_hash_keys[i].ulen);
365 : }
366 : #endif
367 : }
368 1161 : new_meta->current_field = 0;
369 1161 : new_meta->field_count = meta->field_count;
370 :
371 1161 : new_meta->fields = new_fields;
372 :
373 1161 : DBG_RETURN(new_meta);
374 : }
375 : /* }}} */
376 :
377 : /* {{{ mysqlnd_res_meta::fetch_field */
378 : static const MYSQLND_FIELD *
379 : MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
380 321 : {
381 321 : DBG_ENTER("mysqlnd_res_meta::fetch_field");
382 321 : if (meta->current_field >= meta->field_count) {
383 18 : DBG_INF("no more fields");
384 18 : DBG_RETURN(NULL);
385 : }
386 303 : DBG_INF_FMT("name=%s max_length=%u",
387 : meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
388 : meta->fields[meta->current_field].max_length);
389 303 : DBG_RETURN(&meta->fields[meta->current_field++]);
390 : }
391 : /* }}} */
392 :
393 :
394 : /* {{{ mysqlnd_res_meta::fetch_field_direct */
395 : static const MYSQLND_FIELD *
396 : MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta,
397 : MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
398 1076 : {
399 1076 : DBG_ENTER("mysqlnd_res_meta::fetch_field_direct");
400 1076 : DBG_INF_FMT("fieldnr=%d", fieldnr);
401 1076 : DBG_INF_FMT("name=%s max_length=%u",
402 : meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
403 : meta->fields[meta->current_field].max_length);
404 1076 : DBG_RETURN(&meta->fields[fieldnr]);
405 : }
406 : /* }}} */
407 :
408 :
409 : /* {{{ mysqlnd_res_meta::fetch_fields */
410 : static const MYSQLND_FIELD *
411 : MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
412 856 : {
413 856 : DBG_ENTER("mysqlnd_res_meta::fetch_fields");
414 856 : DBG_RETURN(meta->fields);
415 : }
416 : /* }}} */
417 :
418 :
419 : /* {{{ mysqlnd_res_meta::field_tell */
420 : static MYSQLND_FIELD_OFFSET
421 : MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const meta)
422 163 : {
423 163 : return meta->current_field;
424 : }
425 : /* }}} */
426 :
427 :
428 : MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta)
429 : MYSQLND_METHOD(mysqlnd_res_meta, fetch_field),
430 : MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct),
431 : MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields),
432 : MYSQLND_METHOD(mysqlnd_res_meta, field_tell),
433 : MYSQLND_METHOD(mysqlnd_res_meta, read_metadata),
434 : MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata),
435 : MYSQLND_METHOD(mysqlnd_res_meta, free),
436 : MYSQLND_CLASS_METHODS_END;
437 :
438 :
439 : /* {{{ mysqlnd_result_meta_init */
440 : MYSQLND_RES_METADATA *
441 : mysqlnd_result_meta_init(unsigned int field_count TSRMLS_DC)
442 6784 : {
443 : MYSQLND_RES_METADATA *ret;
444 6784 : DBG_ENTER("mysqlnd_result_meta_init");
445 :
446 : /* +1 is to have empty marker at the end */
447 6784 : ret = mnd_ecalloc(1, sizeof(MYSQLND_RES_METADATA));
448 6784 : ret->field_count = field_count;
449 6784 : ret->fields = mnd_ecalloc(field_count + 1, sizeof(MYSQLND_FIELD));
450 6784 : ret->zend_hash_keys = mnd_ecalloc(field_count, sizeof(struct mysqlnd_field_hash_key));
451 :
452 6784 : ret->m = & mysqlnd_mysqlnd_res_meta_methods;
453 6784 : DBG_INF_FMT("meta=%p", ret);
454 6784 : DBG_RETURN(ret);
455 : }
456 :
457 :
458 : /*
459 : * Local variables:
460 : * tab-width: 4
461 : * c-basic-offset: 4
462 : * End:
463 : * vim600: noet sw=4 ts=4 fdm=marker
464 : * vim<600: noet sw=4 ts=4
465 : */
|