Back to index

php5  5.3.10
mysqlnd_result_meta.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 2006-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Georg Richter <georg@mysql.com>                             |
00016   |          Andrey Hristov <andrey@mysql.com>                           |
00017   |          Ulf Wendel <uwendel@mysql.com>                              |
00018   +----------------------------------------------------------------------+
00019 */
00020 
00021 /* $Id: mysqlnd_result_meta.c 321634 2012-01-01 13:15:04Z felipe $ */
00022 #include "php.h"
00023 #include "mysqlnd.h"
00024 #include "mysqlnd_priv.h"
00025 #include "mysqlnd_result.h"
00026 #include "mysqlnd_wireprotocol.h"
00027 #include "mysqlnd_debug.h"
00028 #include "ext/standard/basic_functions.h"
00029 
00030 
00031 /* {{{ php_mysqlnd_free_field_metadata */
00032 static void
00033 php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent TSRMLS_DC)
00034 {
00035        if (meta) {
00036               if (meta->root) {
00037                      mnd_pefree(meta->root, persistent);
00038                      meta->root = NULL;
00039               }
00040               if (meta->def) {
00041                      mnd_pefree(meta->def, persistent);
00042                      meta->def = NULL;
00043               }
00044        }
00045 }
00046 /* }}} */
00047 
00048 
00049 /* {{{ mysqlnd_handle_numeric */
00050 /*
00051   The following code is stolen from ZE - HANDLE_NUMERIC() macro from zend_hash.c
00052   and modified for the needs of mysqlnd.
00053 */
00054 static zend_bool
00055 mysqlnd_is_key_numeric(const char * key, size_t length, long *idx)
00056 {
00057        register const char * tmp = key;
00058 
00059        if (*tmp=='-') {
00060               tmp++;
00061        }
00062        if ((*tmp>='0' && *tmp<='9')) {
00063               do { /* possibly a numeric index */
00064                      const char *end=key+length-1;
00065 
00066                      if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */
00067                             break;
00068                      }
00069                      while (tmp<end) {
00070                             if (!(*tmp>='0' && *tmp<='9')) {
00071                                    break;
00072                             }
00073                             tmp++;
00074                      }
00075                      if (tmp==end && *tmp=='\0') { /* a numeric index */
00076                             if (*key=='-') {
00077                                    *idx = strtol(key, NULL, 10);
00078                                    if (*idx!=LONG_MIN) {
00079                                           return TRUE;
00080                                    }
00081                             } else {
00082                                    *idx = strtol(key, NULL, 10);
00083                                    if (*idx!=LONG_MAX) {
00084                                           return TRUE;
00085                                    }
00086                             }
00087                      }
00088               } while (0);
00089        }
00090        return FALSE;
00091 }
00092 /* }}} */
00093 
00094 
00095 #if MYSQLND_UNICODE
00096 /* {{{ mysqlnd_unicode_is_key_numeric */
00097 static zend_bool
00098 mysqlnd_unicode_is_key_numeric(UChar *key, size_t length, long *idx)
00099 {
00100        register UChar * tmp=key;
00101 
00102        if (*tmp==0x2D /*'-'*/) {
00103               tmp++;
00104        }
00105        if ((*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) { /* possibly a numeric index */
00106               do {
00107                      UChar *end=key+length-1;
00108 
00109                      if (*tmp++==0x30 && length>2) { /* don't accept numbers with leading zeros */
00110                             break;
00111                      }
00112                      while (tmp<end) {
00113                             if (!(*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) {
00114                                    break;
00115                             }
00116                             tmp++;
00117                      }
00118                      if (tmp==end && *tmp==0) { /* a numeric index */
00119                             if (*key==0x2D /*'-'*/) {
00120                                    *idx = zend_u_strtol(key, NULL, 10);
00121                                    if (*idx!=LONG_MIN) {
00122                                           return TRUE;
00123                                    }
00124                             } else {
00125                                    *idx = zend_u_strtol(key, NULL, 10);
00126                                    if (*idx!=LONG_MAX) {
00127                                           return TRUE;
00128                                    }
00129                             }
00130                      }
00131               } while (0);
00132        }
00133        return FALSE;
00134 }
00135 /* }}} */
00136 #endif
00137 
00138 
00139 /* {{{ mysqlnd_res_meta::read_metadata */
00140 static enum_func_status
00141 MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND *conn TSRMLS_DC)
00142 {
00143        unsigned int i = 0;
00144        MYSQLND_PACKET_RES_FIELD * field_packet;
00145 #if MYSQLND_UNICODE
00146        UChar *ustr;
00147        int ulen;
00148 #endif
00149 
00150        DBG_ENTER("mysqlnd_res_meta::read_metadata");
00151 
00152        field_packet = conn->protocol->m.get_result_field_packet(conn->protocol, FALSE TSRMLS_CC);
00153        if (!field_packet) {
00154               SET_OOM_ERROR(conn->error_info);
00155               DBG_RETURN(FAIL);
00156        }
00157        field_packet->persistent_alloc = meta->persistent;
00158        for (;i < meta->field_count; i++) {
00159               long idx;
00160 
00161               if (meta->fields[i].root) {
00162                      /* We re-read metadata for PS */
00163                      mnd_pefree(meta->fields[i].root, meta->persistent);
00164                      meta->fields[i].root = NULL;
00165               }
00166 
00167               field_packet->metadata = &(meta->fields[i]);
00168               if (FAIL == PACKET_READ(field_packet, conn)) {
00169                      PACKET_FREE(field_packet);
00170                      DBG_RETURN(FAIL);
00171               }
00172               if (field_packet->error_info.error_no) {
00173                      conn->error_info = field_packet->error_info;
00174                      /* Return back from CONN_QUERY_SENT */
00175                      PACKET_FREE(field_packet);
00176                      DBG_RETURN(FAIL);
00177               }
00178 
00179               if (field_packet->stupid_list_fields_eof == TRUE) {
00180                      meta->field_count = i;
00181                      break;
00182               }
00183 
00184               if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
00185                      DBG_ERR_FMT("Unknown type %u sent by the server.  Please send a report to the developers",
00186                                           meta->fields[i].type);
00187                      php_error_docref(NULL TSRMLS_CC, E_WARNING,
00188                                                   "Unknown type %u sent by the server. "
00189                                                   "Please send a report to the developers",
00190                                                   meta->fields[i].type);
00191                      PACKET_FREE(field_packet);
00192                      DBG_RETURN(FAIL);
00193               }
00194               if (meta->fields[i].type == MYSQL_TYPE_BIT) {
00195                      size_t field_len;
00196                      DBG_INF("BIT");
00197                      ++meta->bit_fields_count;
00198                      /* .length is in bits */
00199                      field_len = meta->fields[i].length / 8;
00200                      /*
00201                        If there is rest, add one byte :
00202                        8 bits = 1 byte but 9 bits = 2 bytes
00203                      */
00204                      if (meta->fields[i].length % 8) {
00205                             ++field_len;
00206                      }
00207                      switch (field_len) {
00208                             case 8:
00209                             case 7:
00210                             case 6:
00211                             case 5:
00212                                    meta->bit_fields_total_len += 20;/* 21 digis, no sign*/
00213                                    break;
00214                             case 4:
00215                                    meta->bit_fields_total_len += 10;/* 2 000 000 000*/
00216                                    break;
00217                             case 3:
00218                                    meta->bit_fields_total_len += 8;/*  12 000 000*/
00219                                    break;
00220                             case 2:
00221                                    meta->bit_fields_total_len += 5;/* 32 500 */
00222                                    break;
00223                             case 1:
00224                                    meta->bit_fields_total_len += 3;/* 120 */
00225                                    break;
00226                      }
00227               }
00228 
00229 #if MYSQLND_UNICODE
00230               zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen,
00231                                                     meta->fields[i].name,
00232                                                     meta->fields[i].name_length TSRMLS_CC);
00233               if ((meta->zend_hash_keys[i].is_numeric =
00234                                           mysqlnd_unicode_is_key_numeric(ustr, ulen + 1, &idx)))
00235               {
00236                      meta->zend_hash_keys[i].key = idx;
00237                      mnd_efree(ustr);
00238               } else {
00239                      meta->zend_hash_keys[i].ustr.u = ustr;
00240                      meta->zend_hash_keys[i].ulen = ulen;
00241                      meta->zend_hash_keys[i].key = zend_u_get_hash_value(IS_UNICODE, ZSTR(ustr), ulen + 1);
00242               }
00243 #else
00244               /* For BC we have to check whether the key is numeric and use it like this */
00245               if ((meta->zend_hash_keys[i].is_numeric =
00246                                    mysqlnd_is_key_numeric(field_packet->metadata->name,
00247                                                                          field_packet->metadata->name_length + 1,
00248                                                                          &idx)))
00249               {
00250                      meta->zend_hash_keys[i].key = idx;
00251               } else {
00252                      meta->zend_hash_keys[i].key =
00253                                    zend_get_hash_value(field_packet->metadata->name,
00254                                                                       field_packet->metadata->name_length + 1);
00255               }
00256 #endif
00257        }
00258        PACKET_FREE(field_packet);
00259 
00260        DBG_RETURN(PASS);
00261 }
00262 /* }}} */
00263 
00264 
00265 /* {{{ mysqlnd_res_meta::free */
00266 static void
00267 MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA * meta TSRMLS_DC)
00268 {
00269        int i;
00270        MYSQLND_FIELD *fields;
00271        DBG_ENTER("mysqlnd_res_meta::free");
00272        DBG_INF_FMT("persistent=%u", meta->persistent);
00273 
00274        if ((fields = meta->fields)) {
00275               DBG_INF("Freeing fields metadata");
00276               i = meta->field_count;
00277               while (i--) {
00278                      php_mysqlnd_free_field_metadata(fields++, meta->persistent TSRMLS_CC);
00279               }
00280               mnd_pefree(meta->fields, meta->persistent);
00281               meta->fields = NULL;
00282        }
00283 
00284        if (meta->zend_hash_keys) {
00285               DBG_INF("Freeing zend_hash_keys");
00286 #if MYSQLND_UNICODE
00287               if (UG(unicode)) {
00288                      for (i = 0; i < meta->field_count; i++) {
00289                             if (meta->zend_hash_keys[i].ustr.v) {
00290                                    mnd_pefree(meta->zend_hash_keys[i].ustr.v, meta->persistent);
00291                             }
00292                      }
00293               }
00294 #endif
00295               mnd_pefree(meta->zend_hash_keys, meta->persistent);
00296               meta->zend_hash_keys = NULL;
00297        }
00298        DBG_INF("Freeing metadata structure");
00299        mnd_pefree(meta, meta->persistent);
00300 
00301        DBG_VOID_RETURN;
00302 }
00303 /* }}} */
00304 
00305 
00306 /* {{{ mysqlnd_res::clone_metadata */
00307 static MYSQLND_RES_METADATA *
00308 MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC)
00309 {
00310        unsigned int i;
00311        /* +1 is to have empty marker at the end */
00312        MYSQLND_RES_METADATA * new_meta = NULL;
00313        MYSQLND_FIELD * new_fields;
00314        MYSQLND_FIELD * orig_fields = meta->fields;
00315        size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key);
00316 
00317        DBG_ENTER("mysqlnd_res_meta::clone_metadata");
00318        DBG_INF_FMT("persistent=%u", persistent);
00319 
00320        new_meta = mnd_pecalloc(1, sizeof(MYSQLND_RES_METADATA), persistent);
00321        if (!new_meta) {
00322               goto oom;
00323        }
00324        new_meta->persistent = persistent;
00325        new_meta->m = meta->m;
00326 
00327        new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent);
00328        if (!new_fields) {
00329               goto oom;
00330        }
00331 
00332        new_meta->zend_hash_keys = mnd_pemalloc(len, persistent);
00333        if (!new_meta->zend_hash_keys) {
00334               goto oom;
00335        }
00336        memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len);
00337 
00338        /*
00339          This will copy also the strings and the root, which we will have
00340          to adjust in the loop
00341        */
00342        memcpy(new_fields, orig_fields, (meta->field_count) * sizeof(MYSQLND_FIELD));
00343        for (i = 0; i < meta->field_count; i++) {
00344               /* First copy the root, then field by field adjust the pointers */
00345               new_fields[i].root = mnd_pemalloc(orig_fields[i].root_len, persistent);
00346               if (!new_fields[i].root) {
00347                      goto oom;
00348               }
00349               memcpy(new_fields[i].root, orig_fields[i].root, new_fields[i].root_len);
00350 
00351               if (orig_fields[i].name && orig_fields[i].name != mysqlnd_empty_string) {
00352                      new_fields[i].name = new_fields[i].root +
00353                                                          (orig_fields[i].name - orig_fields[i].root);
00354               }
00355               if (orig_fields[i].org_name && orig_fields[i].org_name != mysqlnd_empty_string) {
00356                      new_fields[i].org_name = new_fields[i].root +
00357                                                                 (orig_fields[i].org_name - orig_fields[i].root);
00358               }
00359               if (orig_fields[i].table && orig_fields[i].table != mysqlnd_empty_string) {
00360                      new_fields[i].table  = new_fields[i].root +
00361                                                           (orig_fields[i].table - orig_fields[i].root);
00362               }
00363               if (orig_fields[i].org_table && orig_fields[i].org_table != mysqlnd_empty_string) {
00364                      new_fields[i].org_table     = new_fields[i].root +
00365                                                                  (orig_fields[i].org_table - orig_fields[i].root);
00366               }
00367               if (orig_fields[i].db && orig_fields[i].db != mysqlnd_empty_string) {
00368                      new_fields[i].db = new_fields[i].root + (orig_fields[i].db - orig_fields[i].root);
00369               }
00370               if (orig_fields[i].catalog && orig_fields[i].catalog != mysqlnd_empty_string) {
00371                      new_fields[i].catalog = new_fields[i].root + (orig_fields[i].catalog - orig_fields[i].root);
00372               }
00373               /* def is not on the root, if allocated at all */
00374               if (orig_fields[i].def) {
00375                      new_fields[i].def = mnd_pemalloc(orig_fields[i].def_length + 1, persistent);
00376                      if (!new_fields[i].def) {
00377                             goto oom;
00378                      }
00379                      /* copy the trailing \0 too */
00380                      memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1);
00381               }
00382 #if MYSQLND_UNICODE
00383               if (new_meta->zend_hash_keys[i].ustr.u) {
00384                      new_meta->zend_hash_keys[i].ustr.u =
00385                                    eustrndup(new_meta->zend_hash_keys[i].ustr.u, new_meta->zend_hash_keys[i].ulen);
00386                      if (!new_meta->zend_hash_keys[i].ustr.u) {
00387                             goto oom;
00388                      }
00389               }
00390 #endif
00391        }
00392        new_meta->current_field = 0;
00393        new_meta->field_count = meta->field_count;
00394 
00395        new_meta->fields = new_fields;
00396 
00397        DBG_RETURN(new_meta);
00398 oom:
00399        if (new_meta) {
00400               new_meta->m->free_metadata(new_meta TSRMLS_CC);
00401               new_meta = NULL;
00402        }
00403        DBG_RETURN(NULL);
00404 }
00405 /* }}} */
00406 
00407 /* {{{ mysqlnd_res_meta::fetch_field */
00408 static const MYSQLND_FIELD *
00409 MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
00410 {
00411        DBG_ENTER("mysqlnd_res_meta::fetch_field");
00412        if (meta->current_field >= meta->field_count) {
00413               DBG_INF("no more fields");
00414               DBG_RETURN(NULL);
00415        }
00416        DBG_INF_FMT("name=%s max_length=%u",
00417               meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
00418               meta->fields[meta->current_field].max_length);
00419        DBG_RETURN(&meta->fields[meta->current_field++]);
00420 }
00421 /* }}} */
00422 
00423 
00424 /* {{{ mysqlnd_res_meta::fetch_field_direct */
00425 static const MYSQLND_FIELD *
00426 MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
00427 {
00428        DBG_ENTER("mysqlnd_res_meta::fetch_field_direct");
00429        DBG_INF_FMT("fieldnr=%u", fieldnr);
00430        DBG_INF_FMT("name=%s max_length=%u",
00431               meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
00432               meta->fields[meta->current_field].max_length);
00433        DBG_RETURN(&meta->fields[fieldnr]);
00434 }
00435 /* }}} */
00436 
00437 
00438 /* {{{ mysqlnd_res_meta::fetch_fields */
00439 static const MYSQLND_FIELD *
00440 MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
00441 {
00442        DBG_ENTER("mysqlnd_res_meta::fetch_fields");
00443        DBG_RETURN(meta->fields);
00444 }
00445 /* }}} */
00446 
00447 
00448 /* {{{ mysqlnd_res_meta::field_tell */
00449 static MYSQLND_FIELD_OFFSET
00450 MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const meta TSRMLS_DC)
00451 {
00452        return meta->current_field;
00453 }
00454 /* }}} */
00455 
00456 
00457 static
00458 MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta)
00459        MYSQLND_METHOD(mysqlnd_res_meta, fetch_field),
00460        MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct),
00461        MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields),
00462        MYSQLND_METHOD(mysqlnd_res_meta, field_tell),
00463        MYSQLND_METHOD(mysqlnd_res_meta, read_metadata),
00464        MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata),
00465        MYSQLND_METHOD(mysqlnd_res_meta, free),
00466 MYSQLND_CLASS_METHODS_END;
00467 
00468 
00469 /* {{{ mysqlnd_result_meta_init */
00470 PHPAPI MYSQLND_RES_METADATA *
00471 mysqlnd_result_meta_init(unsigned int field_count, zend_bool persistent TSRMLS_DC)
00472 {
00473        size_t alloc_size = sizeof(MYSQLND_RES_METADATA) + mysqlnd_plugin_count() * sizeof(void *);
00474        MYSQLND_RES_METADATA *ret = mnd_pecalloc(1, alloc_size, persistent);
00475        DBG_ENTER("mysqlnd_result_meta_init");
00476        DBG_INF_FMT("persistent=%u", persistent);
00477 
00478        do {
00479               if (!ret) {
00480                      break;
00481               }
00482               ret->m = & mysqlnd_mysqlnd_res_meta_methods;
00483 
00484               ret->persistent = persistent;
00485               ret->field_count = field_count;
00486               /* +1 is to have empty marker at the end */
00487               ret->fields = mnd_pecalloc(field_count + 1, sizeof(MYSQLND_FIELD), ret->persistent);
00488               ret->zend_hash_keys = mnd_pecalloc(field_count, sizeof(struct mysqlnd_field_hash_key), ret->persistent);
00489               if (!ret->fields || !ret->zend_hash_keys) {
00490                      break;
00491               }
00492               DBG_INF_FMT("meta=%p", ret);
00493               DBG_RETURN(ret);
00494        } while (0);
00495        if (ret) {
00496               ret->m->free_metadata(ret TSRMLS_CC);
00497        }
00498        DBG_RETURN(NULL);
00499 }
00500 /* }}} */
00501 
00502 
00503 /* {{{ mysqlnd_res_meta_get_methods */
00504 PHPAPI struct st_mysqlnd_res_meta_methods *
00505 mysqlnd_result_metadata_get_methods()
00506 {
00507        return &mysqlnd_mysqlnd_res_meta_methods;
00508 }
00509 /* }}} */
00510 
00511 
00512 /* {{{ _mysqlnd_plugin_get_plugin_result_metadata_data */
00513 PHPAPI void **
00514 _mysqlnd_plugin_get_plugin_result_metadata_data(const MYSQLND_RES_METADATA * meta, unsigned int plugin_id TSRMLS_DC)
00515 {
00516        DBG_ENTER("_mysqlnd_plugin_get_plugin_result_metadata_data");
00517        DBG_INF_FMT("plugin_id=%u", plugin_id);
00518        if (!meta || plugin_id >= mysqlnd_plugin_count()) {
00519               return NULL;
00520        }
00521        DBG_RETURN((void *)((char *)meta + sizeof(MYSQLND_RES_METADATA) + plugin_id * sizeof(void *)));
00522 }
00523 /* }}} */
00524 
00525 /*
00526  * Local variables:
00527  * tab-width: 4
00528  * c-basic-offset: 4
00529  * End:
00530  * vim600: noet sw=4 ts=4 fdm=marker
00531  * vim<600: noet sw=4 ts=4
00532  */