mysqlnd_result_meta.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2006-2016 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. /* $Id$ */
  21. #include "php.h"
  22. #include "mysqlnd.h"
  23. #include "mysqlnd_priv.h"
  24. #include "mysqlnd_result.h"
  25. #include "mysqlnd_wireprotocol.h"
  26. #include "mysqlnd_debug.h"
  27. #include "ext/standard/basic_functions.h"
  28. /* {{{ php_mysqlnd_free_field_metadata */
  29. static void
  30. php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent TSRMLS_DC)
  31. {
  32. if (meta) {
  33. if (meta->root) {
  34. mnd_pefree(meta->root, persistent);
  35. meta->root = NULL;
  36. }
  37. if (meta->def) {
  38. mnd_pefree(meta->def, persistent);
  39. meta->def = NULL;
  40. }
  41. }
  42. }
  43. /* }}} */
  44. /* {{{ mysqlnd_handle_numeric */
  45. /*
  46. The following code is stolen from ZE - HANDLE_NUMERIC() macro from zend_hash.c
  47. and modified for the needs of mysqlnd.
  48. */
  49. static zend_bool
  50. mysqlnd_is_key_numeric(const char * key, size_t length, long *idx)
  51. {
  52. register const char * tmp = key;
  53. if (*tmp=='-') {
  54. tmp++;
  55. }
  56. if ((*tmp>='0' && *tmp<='9')) {
  57. do { /* possibly a numeric index */
  58. const char *end=key+length-1;
  59. if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */
  60. break;
  61. }
  62. while (tmp<end) {
  63. if (!(*tmp>='0' && *tmp<='9')) {
  64. break;
  65. }
  66. tmp++;
  67. }
  68. if (tmp==end && *tmp=='\0') { /* a numeric index */
  69. if (*key=='-') {
  70. *idx = strtol(key, NULL, 10);
  71. if (*idx!=LONG_MIN) {
  72. return TRUE;
  73. }
  74. } else {
  75. *idx = strtol(key, NULL, 10);
  76. if (*idx!=LONG_MAX) {
  77. return TRUE;
  78. }
  79. }
  80. }
  81. } while (0);
  82. }
  83. return FALSE;
  84. }
  85. /* }}} */
  86. /* {{{ mysqlnd_res_meta::read_metadata */
  87. static enum_func_status
  88. MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn TSRMLS_DC)
  89. {
  90. unsigned int i = 0;
  91. MYSQLND_PACKET_RES_FIELD * field_packet;
  92. DBG_ENTER("mysqlnd_res_meta::read_metadata");
  93. field_packet = conn->protocol->m.get_result_field_packet(conn->protocol, FALSE TSRMLS_CC);
  94. if (!field_packet) {
  95. SET_OOM_ERROR(*conn->error_info);
  96. DBG_RETURN(FAIL);
  97. }
  98. field_packet->persistent_alloc = meta->persistent;
  99. for (;i < meta->field_count; i++) {
  100. long idx;
  101. if (meta->fields[i].root) {
  102. /* We re-read metadata for PS */
  103. mnd_pefree(meta->fields[i].root, meta->persistent);
  104. meta->fields[i].root = NULL;
  105. }
  106. field_packet->metadata = &(meta->fields[i]);
  107. if (FAIL == PACKET_READ(field_packet, conn)) {
  108. PACKET_FREE(field_packet);
  109. DBG_RETURN(FAIL);
  110. }
  111. if (field_packet->error_info.error_no) {
  112. COPY_CLIENT_ERROR(*conn->error_info, field_packet->error_info);
  113. /* Return back from CONN_QUERY_SENT */
  114. PACKET_FREE(field_packet);
  115. DBG_RETURN(FAIL);
  116. }
  117. if (field_packet->stupid_list_fields_eof == TRUE) {
  118. meta->field_count = i;
  119. break;
  120. }
  121. if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
  122. DBG_ERR_FMT("Unknown type %u sent by the server. Please send a report to the developers",
  123. meta->fields[i].type);
  124. php_error_docref(NULL TSRMLS_CC, E_WARNING,
  125. "Unknown type %u sent by the server. "
  126. "Please send a report to the developers",
  127. meta->fields[i].type);
  128. PACKET_FREE(field_packet);
  129. DBG_RETURN(FAIL);
  130. }
  131. if (meta->fields[i].type == MYSQL_TYPE_BIT) {
  132. size_t field_len;
  133. DBG_INF("BIT");
  134. ++meta->bit_fields_count;
  135. /* .length is in bits */
  136. field_len = meta->fields[i].length / 8;
  137. /*
  138. If there is rest, add one byte :
  139. 8 bits = 1 byte but 9 bits = 2 bytes
  140. */
  141. if (meta->fields[i].length % 8) {
  142. ++field_len;
  143. }
  144. switch (field_len) {
  145. case 8:
  146. case 7:
  147. case 6:
  148. case 5:
  149. meta->bit_fields_total_len += 20;/* 21 digis, no sign*/
  150. break;
  151. case 4:
  152. meta->bit_fields_total_len += 10;/* 2 000 000 000*/
  153. break;
  154. case 3:
  155. meta->bit_fields_total_len += 8;/* 12 000 000*/
  156. break;
  157. case 2:
  158. meta->bit_fields_total_len += 5;/* 32 500 */
  159. break;
  160. case 1:
  161. meta->bit_fields_total_len += 3;/* 120 */
  162. break;
  163. }
  164. }
  165. /* For BC we have to check whether the key is numeric and use it like this */
  166. if ((meta->zend_hash_keys[i].is_numeric =
  167. mysqlnd_is_key_numeric(field_packet->metadata->name,
  168. field_packet->metadata->name_length + 1,
  169. &idx)))
  170. {
  171. meta->zend_hash_keys[i].key = idx;
  172. } else {
  173. meta->zend_hash_keys[i].key =
  174. zend_get_hash_value(field_packet->metadata->name,
  175. field_packet->metadata->name_length + 1);
  176. }
  177. }
  178. PACKET_FREE(field_packet);
  179. DBG_RETURN(PASS);
  180. }
  181. /* }}} */
  182. /* {{{ mysqlnd_res_meta::free */
  183. static void
  184. MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA * meta TSRMLS_DC)
  185. {
  186. int i;
  187. MYSQLND_FIELD *fields;
  188. DBG_ENTER("mysqlnd_res_meta::free");
  189. DBG_INF_FMT("persistent=%u", meta->persistent);
  190. if ((fields = meta->fields)) {
  191. DBG_INF("Freeing fields metadata");
  192. i = meta->field_count;
  193. while (i--) {
  194. php_mysqlnd_free_field_metadata(fields++, meta->persistent TSRMLS_CC);
  195. }
  196. mnd_pefree(meta->fields, meta->persistent);
  197. meta->fields = NULL;
  198. }
  199. if (meta->zend_hash_keys) {
  200. DBG_INF("Freeing zend_hash_keys");
  201. mnd_pefree(meta->zend_hash_keys, meta->persistent);
  202. meta->zend_hash_keys = NULL;
  203. }
  204. DBG_INF("Freeing metadata structure");
  205. mnd_pefree(meta, meta->persistent);
  206. DBG_VOID_RETURN;
  207. }
  208. /* }}} */
  209. /* {{{ mysqlnd_res::clone_metadata */
  210. static MYSQLND_RES_METADATA *
  211. MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC)
  212. {
  213. unsigned int i;
  214. /* +1 is to have empty marker at the end */
  215. MYSQLND_RES_METADATA * new_meta = NULL;
  216. MYSQLND_FIELD * new_fields;
  217. MYSQLND_FIELD * orig_fields = meta->fields;
  218. size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key);
  219. DBG_ENTER("mysqlnd_res_meta::clone_metadata");
  220. DBG_INF_FMT("persistent=%u", persistent);
  221. new_meta = mnd_pecalloc(1, sizeof(MYSQLND_RES_METADATA), persistent);
  222. if (!new_meta) {
  223. goto oom;
  224. }
  225. new_meta->persistent = persistent;
  226. new_meta->m = meta->m;
  227. new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent);
  228. if (!new_fields) {
  229. goto oom;
  230. }
  231. new_meta->zend_hash_keys = mnd_pemalloc(len, persistent);
  232. if (!new_meta->zend_hash_keys) {
  233. goto oom;
  234. }
  235. memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len);
  236. /*
  237. This will copy also the strings and the root, which we will have
  238. to adjust in the loop
  239. */
  240. memcpy(new_fields, orig_fields, (meta->field_count) * sizeof(MYSQLND_FIELD));
  241. for (i = 0; i < meta->field_count; i++) {
  242. /* First copy the root, then field by field adjust the pointers */
  243. new_fields[i].root = mnd_pemalloc(orig_fields[i].root_len, persistent);
  244. if (!new_fields[i].root) {
  245. goto oom;
  246. }
  247. memcpy(new_fields[i].root, orig_fields[i].root, new_fields[i].root_len);
  248. if (orig_fields[i].name && orig_fields[i].name != mysqlnd_empty_string) {
  249. new_fields[i].name = new_fields[i].root +
  250. (orig_fields[i].name - orig_fields[i].root);
  251. }
  252. if (orig_fields[i].org_name && orig_fields[i].org_name != mysqlnd_empty_string) {
  253. new_fields[i].org_name = new_fields[i].root +
  254. (orig_fields[i].org_name - orig_fields[i].root);
  255. }
  256. if (orig_fields[i].table && orig_fields[i].table != mysqlnd_empty_string) {
  257. new_fields[i].table = new_fields[i].root +
  258. (orig_fields[i].table - orig_fields[i].root);
  259. }
  260. if (orig_fields[i].org_table && orig_fields[i].org_table != mysqlnd_empty_string) {
  261. new_fields[i].org_table = new_fields[i].root +
  262. (orig_fields[i].org_table - orig_fields[i].root);
  263. }
  264. if (orig_fields[i].db && orig_fields[i].db != mysqlnd_empty_string) {
  265. new_fields[i].db = new_fields[i].root + (orig_fields[i].db - orig_fields[i].root);
  266. }
  267. if (orig_fields[i].catalog && orig_fields[i].catalog != mysqlnd_empty_string) {
  268. new_fields[i].catalog = new_fields[i].root + (orig_fields[i].catalog - orig_fields[i].root);
  269. }
  270. /* def is not on the root, if allocated at all */
  271. if (orig_fields[i].def) {
  272. new_fields[i].def = mnd_pemalloc(orig_fields[i].def_length + 1, persistent);
  273. if (!new_fields[i].def) {
  274. goto oom;
  275. }
  276. /* copy the trailing \0 too */
  277. memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1);
  278. }
  279. }
  280. new_meta->current_field = 0;
  281. new_meta->field_count = meta->field_count;
  282. new_meta->fields = new_fields;
  283. DBG_RETURN(new_meta);
  284. oom:
  285. if (new_meta) {
  286. new_meta->m->free_metadata(new_meta TSRMLS_CC);
  287. new_meta = NULL;
  288. }
  289. DBG_RETURN(NULL);
  290. }
  291. /* }}} */
  292. /* {{{ mysqlnd_res_meta::fetch_field */
  293. static const MYSQLND_FIELD *
  294. MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
  295. {
  296. DBG_ENTER("mysqlnd_res_meta::fetch_field");
  297. if (meta->current_field >= meta->field_count) {
  298. DBG_INF("no more fields");
  299. DBG_RETURN(NULL);
  300. }
  301. DBG_INF_FMT("name=%s max_length=%u",
  302. meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
  303. meta->fields[meta->current_field].max_length);
  304. DBG_RETURN(&meta->fields[meta->current_field++]);
  305. }
  306. /* }}} */
  307. /* {{{ mysqlnd_res_meta::fetch_field_direct */
  308. static const MYSQLND_FIELD *
  309. MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
  310. {
  311. DBG_ENTER("mysqlnd_res_meta::fetch_field_direct");
  312. DBG_INF_FMT("fieldnr=%u", fieldnr);
  313. DBG_INF_FMT("name=%s max_length=%u",
  314. meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
  315. meta->fields[meta->current_field].max_length);
  316. DBG_RETURN(&meta->fields[fieldnr]);
  317. }
  318. /* }}} */
  319. /* {{{ mysqlnd_res_meta::fetch_fields */
  320. static const MYSQLND_FIELD *
  321. MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
  322. {
  323. DBG_ENTER("mysqlnd_res_meta::fetch_fields");
  324. DBG_RETURN(meta->fields);
  325. }
  326. /* }}} */
  327. /* {{{ mysqlnd_res_meta::field_tell */
  328. static MYSQLND_FIELD_OFFSET
  329. MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const meta TSRMLS_DC)
  330. {
  331. return meta->current_field;
  332. }
  333. /* }}} */
  334. /* {{{ mysqlnd_res_meta::field_seek */
  335. static MYSQLND_FIELD_OFFSET
  336. MYSQLND_METHOD(mysqlnd_res_meta, field_seek)(MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC)
  337. {
  338. MYSQLND_FIELD_OFFSET return_value = 0;
  339. DBG_ENTER("mysqlnd_res_meta::fetch_fields");
  340. return_value = meta->current_field;
  341. meta->current_field = field_offset;
  342. DBG_RETURN(return_value);
  343. }
  344. /* }}} */
  345. static
  346. MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta)
  347. MYSQLND_METHOD(mysqlnd_res_meta, fetch_field),
  348. MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct),
  349. MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields),
  350. MYSQLND_METHOD(mysqlnd_res_meta, field_tell),
  351. MYSQLND_METHOD(mysqlnd_res_meta, field_seek),
  352. MYSQLND_METHOD(mysqlnd_res_meta, read_metadata),
  353. MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata),
  354. MYSQLND_METHOD(mysqlnd_res_meta, free),
  355. MYSQLND_CLASS_METHODS_END;
  356. /* {{{ mysqlnd_result_meta_init */
  357. PHPAPI MYSQLND_RES_METADATA *
  358. mysqlnd_result_meta_init(unsigned int field_count, zend_bool persistent TSRMLS_DC)
  359. {
  360. size_t alloc_size = sizeof(MYSQLND_RES_METADATA) + mysqlnd_plugin_count() * sizeof(void *);
  361. MYSQLND_RES_METADATA *ret = mnd_pecalloc(1, alloc_size, persistent);
  362. DBG_ENTER("mysqlnd_result_meta_init");
  363. DBG_INF_FMT("persistent=%u", persistent);
  364. do {
  365. if (!ret) {
  366. break;
  367. }
  368. ret->m = & mysqlnd_mysqlnd_res_meta_methods;
  369. ret->persistent = persistent;
  370. ret->field_count = field_count;
  371. /* +1 is to have empty marker at the end */
  372. ret->fields = mnd_pecalloc(field_count + 1, sizeof(MYSQLND_FIELD), ret->persistent);
  373. ret->zend_hash_keys = mnd_pecalloc(field_count, sizeof(struct mysqlnd_field_hash_key), ret->persistent);
  374. if (!ret->fields || !ret->zend_hash_keys) {
  375. break;
  376. }
  377. DBG_INF_FMT("meta=%p", ret);
  378. DBG_RETURN(ret);
  379. } while (0);
  380. if (ret) {
  381. ret->m->free_metadata(ret TSRMLS_CC);
  382. }
  383. DBG_RETURN(NULL);
  384. }
  385. /* }}} */
  386. /* {{{ mysqlnd_res_meta_get_methods */
  387. PHPAPI struct st_mysqlnd_res_meta_methods *
  388. mysqlnd_result_metadata_get_methods()
  389. {
  390. return &mysqlnd_mysqlnd_res_meta_methods;
  391. }
  392. /* }}} */
  393. /* {{{ _mysqlnd_plugin_get_plugin_result_metadata_data */
  394. PHPAPI void **
  395. _mysqlnd_plugin_get_plugin_result_metadata_data(const MYSQLND_RES_METADATA * meta, unsigned int plugin_id TSRMLS_DC)
  396. {
  397. DBG_ENTER("_mysqlnd_plugin_get_plugin_result_metadata_data");
  398. DBG_INF_FMT("plugin_id=%u", plugin_id);
  399. if (!meta || plugin_id >= mysqlnd_plugin_count()) {
  400. return NULL;
  401. }
  402. DBG_RETURN((void *)((char *)meta + sizeof(MYSQLND_RES_METADATA) + plugin_id * sizeof(void *)));
  403. }
  404. /* }}} */
  405. /*
  406. * Local variables:
  407. * tab-width: 4
  408. * c-basic-offset: 4
  409. * End:
  410. * vim600: noet sw=4 ts=4 fdm=marker
  411. * vim<600: noet sw=4 ts=4
  412. */