PageRenderTime 60ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/ext/mysqlnd/mysqlnd.c

http://github.com/infusion/PHP
C | 2476 lines | 1773 code | 359 blank | 344 comment | 319 complexity | a6f8f0bc2a328e2fcd6340dae271a70f MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2006-2011 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. /* $Id: mysqlnd.c 308671 2011-02-25 12:52:21Z andrey $ */
  21. #include "php.h"
  22. #include "mysqlnd.h"
  23. #include "mysqlnd_wireprotocol.h"
  24. #include "mysqlnd_priv.h"
  25. #include "mysqlnd_result.h"
  26. #include "mysqlnd_statistics.h"
  27. #include "mysqlnd_charset.h"
  28. #include "mysqlnd_debug.h"
  29. /* for php_get_current_user() */
  30. #include "ext/standard/basic_functions.h"
  31. /*
  32. TODO :
  33. - Don't bind so tightly the metadata with the result set. This means
  34. that the metadata reading should not expect a MYSQLND_RES pointer, it
  35. does not need it, but return a pointer to the metadata (MYSQLND_FIELD *).
  36. For normal statements we will then just assign it to a member of
  37. MYSQLND_RES. For PS statements, it will stay as part of the statement
  38. (MYSQLND_STMT) between prepare and execute. At execute the new metadata
  39. will be sent by the server, so we will discard the old one and then
  40. finally attach it to the result set. This will make the code more clean,
  41. as a prepared statement won't have anymore stmt->result != NULL, as it
  42. is now, just to have where to store the metadata.
  43. - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING
  44. terminated by a string with ptr being NULL. Thus, multi-part messages can be
  45. sent to the network like writev() and this can save at least for
  46. mysqlnd_stmt_send_long_data() new malloc. This change will probably make the
  47. code in few other places cleaner.
  48. */
  49. extern MYSQLND_CHARSET *mysqlnd_charsets;
  50. PHPAPI const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
  51. "Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
  52. "store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords "
  53. "flag from your my.cnf file";
  54. PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
  55. PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
  56. PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
  57. PHPAPI MYSQLND_STATS *mysqlnd_global_stats = NULL;
  58. static zend_bool mysqlnd_library_initted = FALSE;
  59. static struct st_mysqlnd_conn_methods *mysqlnd_conn_methods;
  60. /* {{{ mysqlnd_library_end */
  61. PHPAPI void mysqlnd_library_end(TSRMLS_D)
  62. {
  63. if (mysqlnd_library_initted == TRUE) {
  64. mysqlnd_stats_end(mysqlnd_global_stats);
  65. mysqlnd_global_stats = NULL;
  66. mysqlnd_library_initted = FALSE;
  67. }
  68. }
  69. /* }}} */
  70. /* {{{ mysqlnd_conn::free_options */
  71. static void
  72. MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND * conn TSRMLS_DC)
  73. {
  74. zend_bool pers = conn->persistent;
  75. if (conn->options.charset_name) {
  76. mnd_pefree(conn->options.charset_name, pers);
  77. conn->options.charset_name = NULL;
  78. }
  79. if (conn->options.num_commands) {
  80. unsigned int i;
  81. for (i = 0; i < conn->options.num_commands; i++) {
  82. /* allocated with pestrdup */
  83. mnd_pefree(conn->options.init_commands[i], pers);
  84. }
  85. mnd_pefree(conn->options.init_commands, pers);
  86. conn->options.init_commands = NULL;
  87. }
  88. if (conn->options.cfg_file) {
  89. mnd_pefree(conn->options.cfg_file, pers);
  90. conn->options.cfg_file = NULL;
  91. }
  92. if (conn->options.cfg_section) {
  93. mnd_pefree(conn->options.cfg_section, pers);
  94. conn->options.cfg_section = NULL;
  95. }
  96. }
  97. /* }}} */
  98. /* {{{ mysqlnd_conn::free_contents */
  99. static void
  100. MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND * conn TSRMLS_DC)
  101. {
  102. zend_bool pers = conn->persistent;
  103. DBG_ENTER("mysqlnd_conn::free_contents");
  104. mysqlnd_local_infile_default(conn);
  105. if (conn->current_result) {
  106. conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
  107. conn->current_result = NULL;
  108. }
  109. if (conn->net) {
  110. conn->net->m.free_contents(conn->net TSRMLS_CC);
  111. }
  112. DBG_INF("Freeing memory of members");
  113. if (conn->host) {
  114. DBG_INF("Freeing host");
  115. mnd_pefree(conn->host, pers);
  116. conn->host = NULL;
  117. }
  118. if (conn->user) {
  119. DBG_INF("Freeing user");
  120. mnd_pefree(conn->user, pers);
  121. conn->user = NULL;
  122. }
  123. if (conn->passwd) {
  124. DBG_INF("Freeing passwd");
  125. mnd_pefree(conn->passwd, pers);
  126. conn->passwd = NULL;
  127. }
  128. if (conn->connect_or_select_db) {
  129. DBG_INF("Freeing connect_or_select_db");
  130. mnd_pefree(conn->connect_or_select_db, pers);
  131. conn->connect_or_select_db = NULL;
  132. }
  133. if (conn->unix_socket) {
  134. DBG_INF("Freeing unix_socket");
  135. mnd_pefree(conn->unix_socket, pers);
  136. conn->unix_socket = NULL;
  137. }
  138. if (conn->scheme) {
  139. DBG_INF("Freeing scheme");
  140. mnd_pefree(conn->scheme, pers);
  141. conn->scheme = NULL;
  142. }
  143. if (conn->server_version) {
  144. DBG_INF("Freeing server_version");
  145. mnd_pefree(conn->server_version, pers);
  146. conn->server_version = NULL;
  147. }
  148. if (conn->host_info) {
  149. DBG_INF("Freeing host_info");
  150. mnd_pefree(conn->host_info, pers);
  151. conn->host_info = NULL;
  152. }
  153. if (conn->scramble) {
  154. DBG_INF("Freeing scramble");
  155. mnd_pefree(conn->scramble, pers);
  156. conn->scramble = NULL;
  157. }
  158. if (conn->last_message) {
  159. mnd_pefree(conn->last_message, pers);
  160. conn->last_message = NULL;
  161. }
  162. conn->charset = NULL;
  163. conn->greet_charset = NULL;
  164. DBG_VOID_RETURN;
  165. }
  166. /* }}} */
  167. /* {{{ mysqlnd_conn::dtor */
  168. static void
  169. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn TSRMLS_DC)
  170. {
  171. DBG_ENTER("mysqlnd_conn::dtor");
  172. DBG_INF_FMT("conn=%llu", conn->thread_id);
  173. conn->m->free_contents(conn TSRMLS_CC);
  174. conn->m->free_options(conn TSRMLS_CC);
  175. if (conn->net) {
  176. DBG_INF("Freeing net");
  177. mysqlnd_net_free(conn->net TSRMLS_CC);
  178. conn->net = NULL;
  179. }
  180. if (conn->protocol) {
  181. DBG_INF("Freeing protocol");
  182. mysqlnd_protocol_free(conn->protocol TSRMLS_CC);
  183. conn->protocol = NULL;
  184. }
  185. if (conn->stats) {
  186. mysqlnd_stats_end(conn->stats);
  187. }
  188. mnd_pefree(conn, conn->persistent);
  189. DBG_VOID_RETURN;
  190. }
  191. /* }}} */
  192. /* {{{ mysqlnd_conn::simple_command_handle_response */
  193. static enum_func_status
  194. MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND * conn, enum mysqlnd_packet_type ok_packet,
  195. zend_bool silent, enum php_mysqlnd_server_command command,
  196. zend_bool ignore_upsert_status TSRMLS_DC)
  197. {
  198. enum_func_status ret = FAIL;
  199. DBG_ENTER("mysqlnd_conn::simple_command_handle_response");
  200. DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
  201. switch (ok_packet) {
  202. case PROT_OK_PACKET:{
  203. MYSQLND_PACKET_OK * ok_response = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
  204. if (!ok_response) {
  205. SET_OOM_ERROR(conn->error_info);
  206. break;
  207. }
  208. if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
  209. if (!silent) {
  210. DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]);
  211. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%u",
  212. mysqlnd_command_to_text[command], getpid());
  213. }
  214. } else {
  215. DBG_INF_FMT("OK from server");
  216. if (0xFF == ok_response->field_count) {
  217. /* The server signalled error. Set the error */
  218. SET_CLIENT_ERROR(conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
  219. ret = FAIL;
  220. /*
  221. Cover a protocol design error: error packet does not
  222. contain the server status. Therefore, the client has no way
  223. to find out whether there are more result sets of
  224. a multiple-result-set statement pending. Luckily, in 5.0 an
  225. error always aborts execution of a statement, wherever it is
  226. a multi-statement or a stored procedure, so it should be
  227. safe to unconditionally turn off the flag here.
  228. */
  229. conn->upsert_status.server_status &= ~SERVER_MORE_RESULTS_EXISTS;
  230. SET_ERROR_AFF_ROWS(conn);
  231. } else {
  232. SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
  233. ok_response->message, ok_response->message_len,
  234. conn->persistent);
  235. if (!ignore_upsert_status) {
  236. conn->upsert_status.warning_count = ok_response->warning_count;
  237. conn->upsert_status.server_status = ok_response->server_status;
  238. conn->upsert_status.affected_rows = ok_response->affected_rows;
  239. conn->upsert_status.last_insert_id = ok_response->last_insert_id;
  240. }
  241. }
  242. }
  243. PACKET_FREE(ok_response);
  244. break;
  245. }
  246. case PROT_EOF_PACKET:{
  247. MYSQLND_PACKET_EOF * ok_response = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC);
  248. if (!ok_response) {
  249. SET_OOM_ERROR(conn->error_info);
  250. break;
  251. }
  252. if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
  253. SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE,
  254. "Malformed packet");
  255. if (!silent) {
  256. DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]);
  257. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet. PID=%d",
  258. mysqlnd_command_to_text[command], getpid());
  259. }
  260. } else if (0xFF == ok_response->field_count) {
  261. /* The server signalled error. Set the error */
  262. SET_CLIENT_ERROR(conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
  263. SET_ERROR_AFF_ROWS(conn);
  264. } else if (0xFE != ok_response->field_count) {
  265. SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
  266. if (!silent) {
  267. DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count);
  268. php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X",
  269. ok_response->field_count);
  270. }
  271. } else {
  272. DBG_INF_FMT("OK from server");
  273. }
  274. PACKET_FREE(ok_response);
  275. break;
  276. }
  277. default:
  278. SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
  279. php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
  280. break;
  281. }
  282. DBG_INF(ret == PASS ? "PASS":"FAIL");
  283. DBG_RETURN(ret);
  284. }
  285. /* }}} */
  286. /* {{{ mysqlnd_conn::simple_command */
  287. static enum_func_status
  288. MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND * conn, enum php_mysqlnd_server_command command,
  289. const char * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent,
  290. zend_bool ignore_upsert_status TSRMLS_DC)
  291. {
  292. enum_func_status ret = PASS;
  293. MYSQLND_PACKET_COMMAND * cmd_packet;
  294. DBG_ENTER("mysqlnd_conn::simple_command");
  295. DBG_INF_FMT("command=%s ok_packet=%u silent=%u", mysqlnd_command_to_text[command], ok_packet, silent);
  296. switch (CONN_GET_STATE(conn)) {
  297. case CONN_READY:
  298. break;
  299. case CONN_QUIT_SENT:
  300. SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
  301. DBG_ERR("Server is gone");
  302. DBG_RETURN(FAIL);
  303. default:
  304. SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
  305. DBG_ERR_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn));
  306. DBG_RETURN(FAIL);
  307. }
  308. /* clean UPSERT info */
  309. if (!ignore_upsert_status) {
  310. memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
  311. }
  312. SET_ERROR_AFF_ROWS(conn);
  313. SET_EMPTY_ERROR(conn->error_info);
  314. cmd_packet = conn->protocol->m.get_command_packet(conn->protocol, FALSE TSRMLS_CC);
  315. if (!cmd_packet) {
  316. SET_OOM_ERROR(conn->error_info);
  317. DBG_RETURN(FAIL);
  318. }
  319. cmd_packet->command = command;
  320. if (arg && arg_len) {
  321. cmd_packet->argument = arg;
  322. cmd_packet->arg_len = arg_len;
  323. }
  324. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
  325. if (! PACKET_WRITE(cmd_packet, conn)) {
  326. if (!silent) {
  327. DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
  328. php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
  329. }
  330. DBG_ERR("Server is gone");
  331. ret = FAIL;
  332. } else if (ok_packet != PROT_LAST) {
  333. ret = conn->m->simple_command_handle_response(conn, ok_packet, silent, command, ignore_upsert_status TSRMLS_CC);
  334. }
  335. PACKET_FREE(cmd_packet);
  336. DBG_INF(ret == PASS ? "PASS":"FAIL");
  337. DBG_RETURN(ret);
  338. }
  339. /* }}} */
  340. /* {{{ mysqlnd_conn::set_server_option */
  341. static enum_func_status
  342. MYSQLND_METHOD(mysqlnd_conn, set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC)
  343. {
  344. enum_func_status ret;
  345. char buffer[2];
  346. DBG_ENTER("mysqlnd_conn::set_server_option");
  347. int2store(buffer, (unsigned int) option);
  348. ret = conn->m->simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
  349. DBG_RETURN(ret);
  350. }
  351. /* }}} */
  352. /* {{{ mysqlnd_conn::restart_psession */
  353. static enum_func_status
  354. MYSQLND_METHOD(mysqlnd_conn, restart_psession)(MYSQLND * conn TSRMLS_DC)
  355. {
  356. DBG_ENTER("mysqlnd_conn::restart_psession");
  357. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
  358. /* Free here what should not be seen by the next script */
  359. if (conn->last_message) {
  360. mnd_pefree(conn->last_message, conn->persistent);
  361. conn->last_message = NULL;
  362. }
  363. DBG_RETURN(PASS);
  364. }
  365. /* }}} */
  366. /* {{{ mysqlnd_conn::end_psession */
  367. static enum_func_status
  368. MYSQLND_METHOD(mysqlnd_conn, end_psession)(MYSQLND * conn TSRMLS_DC)
  369. {
  370. DBG_ENTER("mysqlnd_conn::end_psession");
  371. DBG_RETURN(PASS);
  372. }
  373. /* }}} */
  374. #define MYSQLND_ASSEBLED_PACKET_MAX_SIZE 3UL*1024UL*1024UL*1024UL
  375. /* {{{ mysqlnd_connect_run_authentication */
  376. static enum_func_status
  377. mysqlnd_connect_run_authentication(
  378. MYSQLND * conn,
  379. const char * const user,
  380. const char * const passwd,
  381. const char * const db,
  382. size_t db_len,
  383. const MYSQLND_PACKET_GREET * const greet_packet,
  384. const MYSQLND_OPTIONS * const options,
  385. unsigned long mysql_flags
  386. TSRMLS_DC)
  387. {
  388. const MYSQLND_CHARSET * charset = NULL;
  389. enum_func_status ret = FAIL;
  390. MYSQLND_PACKET_AUTH * auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
  391. MYSQLND_PACKET_OK * ok_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
  392. DBG_ENTER("mysqlnd_connect_run_authentication");
  393. if (!auth_packet || !ok_packet) {
  394. SET_OOM_ERROR(conn->error_info);
  395. goto err;
  396. }
  397. #ifdef MYSQLND_SSL_SUPPORTED
  398. if ((greet_packet->server_capabilities & CLIENT_SSL) && (mysql_flags & CLIENT_SSL)) {
  399. auth_packet->send_half_packet = TRUE;
  400. }
  401. #endif
  402. auth_packet->user = user;
  403. auth_packet->password = passwd;
  404. if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
  405. auth_packet->charset_no = charset->nr;
  406. } else {
  407. #if MYSQLND_UNICODE
  408. auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */
  409. #else
  410. auth_packet->charset_no = greet_packet->charset_no;
  411. #endif
  412. }
  413. auth_packet->db = db;
  414. auth_packet->db_len = db_len;
  415. auth_packet->max_packet_size= MYSQLND_ASSEBLED_PACKET_MAX_SIZE;
  416. auth_packet->client_flags= mysql_flags;
  417. conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent);
  418. if (!conn->scramble) {
  419. SET_OOM_ERROR(conn->error_info);
  420. goto err;
  421. }
  422. memcpy(auth_packet->server_scramble_buf, greet_packet->scramble_buf, SCRAMBLE_LENGTH);
  423. if (!PACKET_WRITE(auth_packet, conn)) {
  424. CONN_SET_STATE(conn, CONN_QUIT_SENT);
  425. SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
  426. goto err;
  427. }
  428. #ifdef MYSQLND_SSL_SUPPORTED
  429. if (auth_packet->send_half_packet) {
  430. zend_bool verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? TRUE:FALSE;
  431. DBG_INF("Switching to SSL");
  432. conn->net->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify TSRMLS_CC);
  433. if (FAIL == conn->net->m.enable_ssl(conn->net TSRMLS_CC)) {
  434. goto err;
  435. }
  436. auth_packet->send_half_packet = FALSE;
  437. if (!PACKET_WRITE(auth_packet, conn)) {
  438. CONN_SET_STATE(conn, CONN_QUIT_SENT);
  439. SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
  440. goto err;
  441. }
  442. }
  443. #endif
  444. if (FAIL == PACKET_READ(ok_packet, conn) || ok_packet->field_count >= 0xFE) {
  445. if (ok_packet->field_count == 0xFE) {
  446. /* old authentication with new server !*/
  447. DBG_ERR(mysqlnd_old_passwd);
  448. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
  449. } else if (ok_packet->field_count == 0xFF) {
  450. if (ok_packet->sqlstate[0]) {
  451. strlcpy(conn->error_info.sqlstate, ok_packet->sqlstate, sizeof(conn->error_info.sqlstate));
  452. DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", ok_packet->error_no, ok_packet->sqlstate, ok_packet->error);
  453. }
  454. conn->error_info.error_no = ok_packet->error_no;
  455. strlcpy(conn->error_info.error, ok_packet->error, sizeof(conn->error_info.error));
  456. }
  457. goto err;
  458. }
  459. SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
  460. ok_packet->message, ok_packet->message_len,
  461. conn->persistent);
  462. conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
  463. ret = PASS;
  464. err:
  465. PACKET_FREE(auth_packet);
  466. PACKET_FREE(ok_packet);
  467. DBG_RETURN(ret);
  468. }
  469. /* }}} */
  470. #define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \
  471. CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
  472. CLIENT_MULTI_RESULTS)
  473. /* {{{ mysqlnd_conn::connect */
  474. static enum_func_status
  475. MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn,
  476. const char *host, const char *user,
  477. const char *passwd, unsigned int passwd_len,
  478. const char *db, unsigned int db_len,
  479. unsigned int port,
  480. const char * socket_or_pipe,
  481. unsigned int mysql_flags
  482. TSRMLS_DC)
  483. {
  484. char *errstr = NULL;
  485. int errcode = 0, host_len;
  486. zend_bool unix_socket = FALSE;
  487. zend_bool reconnect = FALSE;
  488. zend_bool saved_compression = FALSE;
  489. MYSQLND_PACKET_GREET * greet_packet = NULL;
  490. DBG_ENTER("mysqlnd_conn::connect");
  491. DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u",
  492. host?host:"", user?user:"", db?db:"", port, mysql_flags,
  493. conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1);
  494. if (conn && CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
  495. DBG_INF("Connecting on a connected handle.");
  496. if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) {
  497. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT);
  498. reconnect = TRUE;
  499. conn->m->send_close(conn TSRMLS_CC);
  500. }
  501. conn->m->free_contents(conn TSRMLS_CC);
  502. MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
  503. if (conn->persistent) {
  504. MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
  505. }
  506. /* Now reconnect using the same handle */
  507. if (conn->net->compressed) {
  508. /*
  509. we need to save the state. As we will re-connect, net->compressed should be off, or
  510. we will look for a compression header as part of the greet message, but there will
  511. be none.
  512. */
  513. saved_compression = TRUE;
  514. conn->net->compressed = FALSE;
  515. }
  516. }
  517. if (!host || !host[0]) {
  518. host = "localhost";
  519. }
  520. if (!user) {
  521. DBG_INF_FMT("no user given, using empty string");
  522. user = "";
  523. }
  524. if (!passwd) {
  525. DBG_INF_FMT("no password given, using empty string");
  526. passwd = "";
  527. passwd_len = 0;
  528. }
  529. if (!db) {
  530. DBG_INF_FMT("no db given, using empty string");
  531. db = "";
  532. db_len = 0;
  533. }
  534. host_len = strlen(host);
  535. {
  536. char * transport = NULL;
  537. int transport_len;
  538. #ifndef PHP_WIN32
  539. if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) {
  540. DBG_INF_FMT("socket=%s", socket_or_pipe? socket_or_pipe:"n/a");
  541. if (!socket_or_pipe) {
  542. socket_or_pipe = "/tmp/mysql.sock";
  543. }
  544. transport_len = spprintf(&transport, 0, "unix://%s", socket_or_pipe);
  545. unix_socket = TRUE;
  546. } else
  547. #endif
  548. {
  549. if (!port) {
  550. port = 3306;
  551. }
  552. transport_len = spprintf(&transport, 0, "tcp://%s:%u", host, port);
  553. }
  554. if (!transport) {
  555. SET_OOM_ERROR(conn->error_info);
  556. goto err; /* OOM */
  557. }
  558. DBG_INF_FMT("transport=%s", transport);
  559. conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent);
  560. conn->scheme_len = transport_len;
  561. efree(transport); /* allocated by spprintf */
  562. transport = NULL;
  563. if (!conn->scheme) {
  564. goto err; /* OOM */
  565. }
  566. }
  567. greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE TSRMLS_CC);
  568. if (!greet_packet) {
  569. SET_OOM_ERROR(conn->error_info);
  570. goto err; /* OOM */
  571. }
  572. if (FAIL == conn->net->m.connect(conn->net, conn->scheme, conn->scheme_len, conn->persistent, &errstr, &errcode TSRMLS_CC)) {
  573. goto err;
  574. }
  575. DBG_INF_FMT("stream=%p", conn->net->stream);
  576. if (FAIL == PACKET_READ(greet_packet, conn)) {
  577. DBG_ERR("Error while reading greeting packet");
  578. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
  579. goto err;
  580. } else if (greet_packet->error_no) {
  581. DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
  582. SET_CLIENT_ERROR(conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
  583. goto err;
  584. } else if (greet_packet->pre41) {
  585. DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
  586. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
  587. " is not supported. Server is %-.32s", greet_packet->server_version);
  588. SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
  589. "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
  590. goto err;
  591. }
  592. conn->thread_id = greet_packet->thread_id;
  593. conn->protocol_version = greet_packet->protocol_version;
  594. conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent);
  595. conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
  596. /* we allow load data local infile by default */
  597. mysql_flags |= CLIENT_LOCAL_FILES | CLIENT_PS_MULTI_RESULTS;
  598. mysql_flags |= MYSQLND_CAPABILITIES;
  599. if (db) {
  600. mysql_flags |= CLIENT_CONNECT_WITH_DB;
  601. }
  602. if (PG(open_basedir) && strlen(PG(open_basedir))) {
  603. mysql_flags ^= CLIENT_LOCAL_FILES;
  604. }
  605. #ifndef MYSQLND_COMPRESSION_ENABLED
  606. if (mysql_flags & CLIENT_COMPRESS) {
  607. mysql_flags &= ~CLIENT_COMPRESS;
  608. }
  609. #else
  610. if (conn->net->options.flags & MYSQLND_NET_FLAG_USE_COMPRESSION) {
  611. mysql_flags |= CLIENT_COMPRESS;
  612. }
  613. #endif
  614. #ifndef MYSQLND_SSL_SUPPORTED
  615. if (mysql_flags & CLIENT_SSL) {
  616. mysql_flags &= ~CLIENT_SSL;
  617. }
  618. #else
  619. if (conn->net->options.ssl_key || conn->net->options.ssl_cert ||
  620. conn->net->options.ssl_ca || conn->net->options.ssl_capath || conn->net->options.ssl_cipher)
  621. {
  622. mysql_flags |= CLIENT_SSL;
  623. }
  624. #endif
  625. /* Default native int and float casting at connection time */
  626. conn->options.int_and_float_native = 1;
  627. if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, greet_packet, &conn->options, mysql_flags TSRMLS_CC)) {
  628. goto err;
  629. }
  630. {
  631. CONN_SET_STATE(conn, CONN_READY);
  632. if (saved_compression) {
  633. conn->net->compressed = TRUE;
  634. }
  635. /*
  636. If a connect on a existing handle is performed and mysql_flags is
  637. passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
  638. which we set based on saved_compression.
  639. */
  640. conn->net->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
  641. conn->user = mnd_pestrdup(user, conn->persistent);
  642. conn->user_len = strlen(conn->user);
  643. conn->passwd = mnd_pestrndup(passwd, passwd_len, conn->persistent);
  644. conn->passwd_len = passwd_len;
  645. conn->port = port;
  646. conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
  647. conn->connect_or_select_db_len = db_len;
  648. if (!conn->user || !conn->passwd || !conn->connect_or_select_db) {
  649. SET_OOM_ERROR(conn->error_info);
  650. goto err; /* OOM */
  651. }
  652. if (!unix_socket) {
  653. conn->host = mnd_pestrdup(host, conn->persistent);
  654. if (!conn->host) {
  655. SET_OOM_ERROR(conn->error_info);
  656. goto err; /* OOM */
  657. }
  658. conn->host_len = strlen(conn->host);
  659. {
  660. char *p;
  661. spprintf(&p, 0, "%s via TCP/IP", conn->host);
  662. if (!p) {
  663. SET_OOM_ERROR(conn->error_info);
  664. goto err; /* OOM */
  665. }
  666. conn->host_info = mnd_pestrdup(p, conn->persistent);
  667. efree(p); /* allocated by spprintf */
  668. if (!conn->host_info) {
  669. SET_OOM_ERROR(conn->error_info);
  670. goto err; /* OOM */
  671. }
  672. }
  673. } else {
  674. conn->unix_socket = mnd_pestrdup(socket_or_pipe, conn->persistent);
  675. conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
  676. if (!conn->unix_socket || !conn->host_info) {
  677. SET_OOM_ERROR(conn->error_info);
  678. goto err; /* OOM */
  679. }
  680. conn->unix_socket_len = strlen(conn->unix_socket);
  681. }
  682. conn->client_flag = mysql_flags;
  683. conn->max_packet_size = MYSQLND_ASSEBLED_PACKET_MAX_SIZE;
  684. /* todo: check if charset is available */
  685. conn->server_capabilities = greet_packet->server_capabilities;
  686. conn->upsert_status.warning_count = 0;
  687. conn->upsert_status.server_status = greet_packet->server_status;
  688. conn->upsert_status.affected_rows = 0;
  689. SET_EMPTY_ERROR(conn->error_info);
  690. mysqlnd_local_infile_default(conn);
  691. #if MYSQLND_UNICODE
  692. {
  693. unsigned int as_unicode = 1;
  694. conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, (char *)&as_unicode TSRMLS_CC);
  695. DBG_INF("unicode set");
  696. }
  697. #endif
  698. if (conn->options.init_commands) {
  699. unsigned int current_command = 0;
  700. for (; current_command < conn->options.num_commands; ++current_command) {
  701. const char * const command = conn->options.init_commands[current_command];
  702. if (command) {
  703. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT);
  704. if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) {
  705. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT);
  706. goto err;
  707. }
  708. if (conn->last_query_type == QUERY_SELECT) {
  709. MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC);
  710. if (result) {
  711. result->m.free_result(result, TRUE TSRMLS_CC);
  712. }
  713. }
  714. }
  715. }
  716. }
  717. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1);
  718. if (reconnect) {
  719. MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);
  720. }
  721. if (conn->persistent) {
  722. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1);
  723. }
  724. DBG_INF_FMT("connection_id=%llu", conn->thread_id);
  725. PACKET_FREE(greet_packet);
  726. DBG_RETURN(PASS);
  727. }
  728. err:
  729. PACKET_FREE(greet_packet);
  730. if (errstr) {
  731. DBG_ERR_FMT("[%u] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme);
  732. SET_CLIENT_ERROR(conn->error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr);
  733. php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme);
  734. /* no mnd_ since we don't allocate it */
  735. efree(errstr);
  736. }
  737. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE);
  738. DBG_RETURN(FAIL);
  739. }
  740. /* }}} */
  741. /* {{{ mysqlnd_connect */
  742. PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn,
  743. const char *host, const char *user,
  744. const char *passwd, unsigned int passwd_len,
  745. const char *db, unsigned int db_len,
  746. unsigned int port,
  747. const char *socket_or_pipe,
  748. unsigned int mysql_flags
  749. TSRMLS_DC)
  750. {
  751. enum_func_status ret = FAIL;
  752. zend_bool self_alloced = FALSE;
  753. DBG_ENTER("mysqlnd_connect");
  754. DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags);
  755. if (!conn) {
  756. self_alloced = TRUE;
  757. if (!(conn = mysqlnd_init(FALSE))) {
  758. /* OOM */
  759. DBG_RETURN(NULL);
  760. }
  761. }
  762. ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
  763. if (ret == FAIL) {
  764. if (self_alloced) {
  765. /*
  766. We have alloced, thus there are no references to this
  767. object - we are free to kill it!
  768. */
  769. conn->m->dtor(conn TSRMLS_CC);
  770. } else {
  771. /* This will also close conn->net->stream if it has been opened */
  772. conn->m->free_contents(conn TSRMLS_CC);
  773. }
  774. DBG_RETURN(NULL);
  775. }
  776. DBG_RETURN(conn);
  777. }
  778. /* }}} */
  779. /* {{{ mysqlnd_conn::query */
  780. /*
  781. If conn->error_info.error_no is not zero, then we had an error.
  782. Still the result from the query is PASS
  783. */
  784. static enum_func_status
  785. MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC)
  786. {
  787. enum_func_status ret;
  788. DBG_ENTER("mysqlnd_conn::query");
  789. DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
  790. if (PASS != conn->m->simple_command(conn, COM_QUERY, query, query_len,
  791. PROT_LAST /* we will handle the OK packet*/,
  792. FALSE, FALSE TSRMLS_CC)) {
  793. DBG_RETURN(FAIL);
  794. }
  795. CONN_SET_STATE(conn, CONN_QUERY_SENT);
  796. /*
  797. Here read the result set. We don't do it in simple_command because it need
  798. information from the ok packet. We will fetch it ourselves.
  799. */
  800. ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC);
  801. if (ret == PASS && conn->last_query_type == QUERY_UPSERT && conn->upsert_status.affected_rows) {
  802. MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status.affected_rows);
  803. }
  804. DBG_RETURN(ret);
  805. }
  806. /* }}} */
  807. /* {{{ mysqlnd_conn::send_query */
  808. static enum_func_status
  809. MYSQLND_METHOD(mysqlnd_conn, send_query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC)
  810. {
  811. enum_func_status ret;
  812. DBG_ENTER("mysqlnd_conn::send_query");
  813. DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
  814. ret = conn->m->simple_command(conn, COM_QUERY, query, query_len,
  815. PROT_LAST /* we will handle the OK packet*/,
  816. FALSE, FALSE TSRMLS_CC);
  817. CONN_SET_STATE(conn, CONN_QUERY_SENT);
  818. DBG_RETURN(ret);
  819. }
  820. /* }}} */
  821. /* {{{ mysqlnd_conn::reap_query */
  822. static enum_func_status
  823. MYSQLND_METHOD(mysqlnd_conn, reap_query)(MYSQLND * conn TSRMLS_DC)
  824. {
  825. enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
  826. DBG_ENTER("mysqlnd_conn::reap_query");
  827. DBG_INF_FMT("conn=%llu", conn->thread_id);
  828. if (state <= CONN_READY || state == CONN_QUIT_SENT) {
  829. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed");
  830. DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
  831. DBG_RETURN(FAIL);
  832. }
  833. DBG_RETURN(conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC));
  834. }
  835. /* }}} */
  836. #include "php_network.h"
  837. MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array TSRMLS_DC)
  838. {
  839. int cnt = 0;
  840. MYSQLND **p = conn_array, **p_p;
  841. MYSQLND **ret = NULL;
  842. while (*p) {
  843. if (CONN_GET_STATE(*p) <= CONN_READY || CONN_GET_STATE(*p) == CONN_QUIT_SENT) {
  844. cnt++;
  845. }
  846. p++;
  847. }
  848. if (cnt) {
  849. MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
  850. p_p = p = conn_array;
  851. while (*p) {
  852. if (CONN_GET_STATE(*p) <= CONN_READY || CONN_GET_STATE(*p) == CONN_QUIT_SENT) {
  853. *ret_p = *p;
  854. *p = NULL;
  855. ret_p++;
  856. } else {
  857. *p_p = *p;
  858. p_p++;
  859. }
  860. p++;
  861. }
  862. *ret_p = NULL;
  863. }
  864. return ret;
  865. }
  866. /* {{{ stream_select mysqlnd_stream_array_to_fd_set functions */
  867. static int mysqlnd_stream_array_to_fd_set(MYSQLND **conn_array, fd_set *fds, php_socket_t *max_fd TSRMLS_DC)
  868. {
  869. php_socket_t this_fd;
  870. int cnt = 0;
  871. MYSQLND **p = conn_array;
  872. while (*p) {
  873. /* get the fd.
  874. * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
  875. * when casting. It is only used here so that the buffered data warning
  876. * is not displayed.
  877. * */
  878. if (SUCCESS == php_stream_cast((*p)->net->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
  879. (void*)&this_fd, 1) && this_fd >= 0) {
  880. PHP_SAFE_FD_SET(this_fd, fds);
  881. if (this_fd > *max_fd) {
  882. *max_fd = this_fd;
  883. }
  884. cnt++;
  885. }
  886. p++;
  887. }
  888. return cnt ? 1 : 0;
  889. }
  890. static int mysqlnd_stream_array_from_fd_set(MYSQLND **conn_array, fd_set *fds TSRMLS_DC)
  891. {
  892. php_socket_t this_fd;
  893. int ret = 0;
  894. zend_bool disproportion = FALSE;
  895. MYSQLND **fwd = conn_array, **bckwd = conn_array;
  896. while (*fwd) {
  897. if (SUCCESS == php_stream_cast((*fwd)->net->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
  898. (void*)&this_fd, 1) && this_fd >= 0) {
  899. if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
  900. if (disproportion) {
  901. *bckwd = *fwd;
  902. }
  903. bckwd++;
  904. fwd++;
  905. ret++;
  906. continue;
  907. }
  908. }
  909. disproportion = TRUE;
  910. fwd++;
  911. }
  912. *bckwd = NULL;/* NULL-terminate the list */
  913. return ret;
  914. }
  915. #ifndef PHP_WIN32
  916. #define php_select(m, r, w, e, t) select(m, r, w, e, t)
  917. #else
  918. #include "win32/select.h"
  919. #endif
  920. /* {{{ _mysqlnd_poll */
  921. PHPAPI enum_func_status
  922. _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, uint * desc_num TSRMLS_DC)
  923. {
  924. struct timeval tv;
  925. struct timeval *tv_p = NULL;
  926. fd_set rfds, wfds, efds;
  927. php_socket_t max_fd = 0;
  928. int retval, sets = 0;
  929. int set_count, max_set_count = 0;
  930. DBG_ENTER("mysqlnd_poll");
  931. if (sec < 0 || usec < 0) {
  932. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values passed for sec and/or usec");
  933. DBG_RETURN(FAIL);
  934. }
  935. *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array TSRMLS_CC);
  936. FD_ZERO(&rfds);
  937. FD_ZERO(&wfds);
  938. FD_ZERO(&efds);
  939. if (r_array != NULL) {
  940. set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
  941. if (set_count > max_set_count) {
  942. max_set_count = set_count;
  943. }
  944. sets += set_count;
  945. }
  946. if (e_array != NULL) {
  947. set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
  948. if (set_count > max_set_count) {
  949. max_set_count = set_count;
  950. }
  951. sets += set_count;
  952. }
  953. if (!sets) {
  954. php_error_docref(NULL TSRMLS_CC, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
  955. DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
  956. DBG_RETURN(FAIL);
  957. }
  958. PHP_SAFE_MAX_FD(max_fd, max_set_count);
  959. /* Solaris + BSD do not like microsecond values which are >= 1 sec */
  960. if (usec > 999999) {
  961. tv.tv_sec = sec + (usec / 1000000);
  962. tv.tv_usec = usec % 1000000;
  963. } else {
  964. tv.tv_sec = sec;
  965. tv.tv_usec = usec;
  966. }
  967. tv_p = &tv;
  968. retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
  969. if (retval == -1) {
  970. php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
  971. errno, strerror(errno), max_fd);
  972. DBG_RETURN(FAIL);
  973. }
  974. if (r_array != NULL) {
  975. mysqlnd_stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
  976. }
  977. if (e_array != NULL) {
  978. mysqlnd_stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
  979. }
  980. *desc_num = retval;
  981. DBG_RETURN(PASS);
  982. }
  983. /* }}} */
  984. /*
  985. COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
  986. - There is no result set header - status from the command, which
  987. impacts us to allocate big chunk of memory for reading the metadata.
  988. - The EOF packet is consumed by the metadata packet reader.
  989. */
  990. /* {{{ mysqlnd_conn::list_fields */
  991. MYSQLND_RES *
  992. MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND * conn, const char *table, const char *achtung_wild TSRMLS_DC)
  993. {
  994. /* db + \0 + wild + \0 (for wild) */
  995. char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
  996. size_t table_len, wild_len;
  997. MYSQLND_RES *result = NULL;
  998. DBG_ENTER("mysqlnd_conn::list_fields");
  999. DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
  1000. p = buff;
  1001. if (table && (table_len = strlen(table))) {
  1002. size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN);
  1003. memcpy(p, table, to_copy);
  1004. p += to_copy;
  1005. *p++ = '\0';
  1006. }
  1007. if (achtung_wild && (wild_len = strlen(achtung_wild))) {
  1008. size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
  1009. memcpy(p, achtung_wild, to_copy);
  1010. p += to_copy;
  1011. *p++ = '\0';
  1012. }
  1013. if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
  1014. PROT_LAST /* we will handle the OK packet*/,
  1015. FALSE, TRUE TSRMLS_CC)) {
  1016. DBG_RETURN(NULL);
  1017. }
  1018. /*
  1019. Prepare for the worst case.
  1020. MyISAM goes to 2500 BIT columns, double it for safety.
  1021. */
  1022. result = conn->m->result_init(5000, conn->persistent TSRMLS_CC);
  1023. if (!result) {
  1024. DBG_RETURN(NULL);
  1025. }
  1026. if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) {
  1027. DBG_ERR("Error ocurred while reading metadata");
  1028. result->m.free_result(result, TRUE TSRMLS_CC);
  1029. DBG_RETURN(NULL);
  1030. }
  1031. result->type = MYSQLND_RES_NORMAL;
  1032. result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
  1033. result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
  1034. if (!result->unbuf) {
  1035. /* OOM */
  1036. SET_OOM_ERROR(conn->error_info);
  1037. result->m.free_result(result, TRUE TSRMLS_CC);
  1038. DBG_RETURN(NULL);
  1039. }
  1040. result->unbuf->eof_reached = TRUE;
  1041. DBG_RETURN(result);
  1042. }
  1043. /* }}} */
  1044. /* {{{ mysqlnd_conn::list_method */
  1045. MYSQLND_RES *
  1046. MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND * conn, const char * query, const char *achtung_wild, char *par1 TSRMLS_DC)
  1047. {
  1048. char *show_query = NULL;
  1049. size_t show_query_len;
  1050. MYSQLND_RES *result = NULL;
  1051. DBG_ENTER("mysqlnd_conn::list_method");
  1052. DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
  1053. if (par1) {
  1054. if (achtung_wild) {
  1055. show_query_len = spprintf(&show_query, 0, query, par1, achtung_wild);
  1056. } else {
  1057. show_query_len = spprintf(&show_query, 0, query, par1);
  1058. }
  1059. } else {
  1060. if (achtung_wild) {
  1061. show_query_len = spprintf(&show_query, 0, query, achtung_wild);
  1062. } else {
  1063. show_query_len = strlen(show_query = (char *)query);
  1064. }
  1065. }
  1066. if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) {
  1067. result = conn->m->store_result(conn TSRMLS_CC);
  1068. }
  1069. if (show_query != query) {
  1070. efree(show_query); /* allocated by spprintf */
  1071. }
  1072. DBG_RETURN(result);
  1073. }
  1074. /* }}} */
  1075. /* {{{ mysqlnd_conn::errno */
  1076. static unsigned int
  1077. MYSQLND_METHOD(mysqlnd_conn, errno)(const MYSQLND * const conn TSRMLS_DC)
  1078. {
  1079. return conn->error_info.error_no;
  1080. }
  1081. /* }}} */
  1082. /* {{{ mysqlnd_conn::error */
  1083. static const char *
  1084. MYSQLND_METHOD(mysqlnd_conn, error)(const MYSQLND * const conn TSRMLS_DC)
  1085. {
  1086. return conn->error_info.error;
  1087. }
  1088. /* }}} */
  1089. /* {{{ mysqlnd_conn::sqlstate */
  1090. static const char *
  1091. MYSQLND_METHOD(mysqlnd_conn, sqlstate)(const MYSQLND * const conn TSRMLS_DC)
  1092. {
  1093. return conn->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL;
  1094. }
  1095. /* }}} */
  1096. /* {{{ mysqlnd_old_escape_string */
  1097. PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC)
  1098. {
  1099. DBG_ENTER("mysqlnd_old_escape_string");
  1100. DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len TSRMLS_CC));
  1101. }
  1102. /* }}} */
  1103. /* {{{ mysqlnd_conn::ssl_set */
  1104. static enum_func_status
  1105. MYSQLND_METHOD(mysqlnd_conn, ssl_set)(MYSQLND * const conn, const char * key, const char * const cert, const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC)
  1106. {
  1107. return (PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC) &&
  1108. PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC) &&
  1109. PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC) &&
  1110. PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC) &&
  1111. PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC)) ? PASS : FAIL;
  1112. }
  1113. /* }}} */
  1114. /* {{{ mysqlnd_conn::escape_string */
  1115. static ulong
  1116. MYSQLND_METHOD(mysqlnd_conn, escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC)
  1117. {
  1118. DBG_ENTER("mysqlnd_conn::escape_string");
  1119. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1120. if (conn->upsert_status.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
  1121. DBG_RETURN(mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC));
  1122. }
  1123. DBG_RETURN(mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC));
  1124. }
  1125. /* }}} */
  1126. /* {{{ mysqlnd_conn::dump_debug_info */
  1127. static enum_func_status
  1128. MYSQLND_METHOD(mysqlnd_conn, dump_debug_info)(MYSQLND * const conn TSRMLS_DC)
  1129. {
  1130. DBG_ENTER("mysqlnd_conn::dump_debug_info");
  1131. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1132. DBG_RETURN(conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC));
  1133. }
  1134. /* }}} */
  1135. /* {{{ mysqlnd_conn::select_db */
  1136. static enum_func_status
  1137. MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC)
  1138. {
  1139. enum_func_status ret;
  1140. DBG_ENTER("mysqlnd_conn::select_db");
  1141. DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
  1142. ret = conn->m->simple_command(conn, COM_INIT_DB, db, db_len, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
  1143. /*
  1144. The server sends 0 but libmysql doesn't read it and has established
  1145. a protocol of giving back -1. Thus we have to follow it :(
  1146. */
  1147. SET_ERROR_AFF_ROWS(conn);
  1148. if (ret == PASS) {
  1149. if (conn->connect_or_select_db) {
  1150. mnd_pefree(conn->connect_or_select_db, conn->persistent);
  1151. }
  1152. conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
  1153. conn->connect_or_select_db_len = db_len;
  1154. if (!conn->connect_or_select_db) {
  1155. /* OOM */
  1156. SET_OOM_ERROR(conn->error_info);
  1157. ret = FAIL;
  1158. }
  1159. }
  1160. DBG_RETURN(ret);
  1161. }
  1162. /* }}} */
  1163. /* {{{ mysqlnd_conn::ping */
  1164. static enum_func_status
  1165. MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC)
  1166. {
  1167. enum_func_status ret;
  1168. DBG_ENTER("mysqlnd_conn::ping");
  1169. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1170. ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE TSRMLS_CC);
  1171. /*
  1172. The server sends 0 but libmysql doesn't read it and has established
  1173. a protocol of giving back -1. Thus we have to follow it :(
  1174. */
  1175. SET_ERROR_AFF_ROWS(conn);
  1176. DBG_INF_FMT("ret=%u", ret);
  1177. DBG_RETURN(ret);
  1178. }
  1179. /* }}} */
  1180. /* {{{ mysqlnd_conn::statistic */
  1181. static enum_func_status
  1182. MYSQLND_METHOD(mysqlnd_conn, statistic)(MYSQLND * conn, char **message, unsigned int * message_len TSRMLS_DC)
  1183. {
  1184. enum_func_status ret;
  1185. MYSQLND_PACKET_STATS * stats_header;
  1186. DBG_ENTER("mysqlnd_conn::statistic");
  1187. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1188. ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE TSRMLS_CC);
  1189. if (FAIL == ret) {
  1190. DBG_RETURN(FAIL);
  1191. }
  1192. stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE TSRMLS_CC);
  1193. if (!stats_header) {
  1194. SET_OOM_ERROR(conn->error_info);
  1195. DBG_RETURN(FAIL);
  1196. }
  1197. if (FAIL == (ret = PACKET_READ(stats_header, conn))) {
  1198. DBG_RETURN(FAIL);
  1199. }
  1200. /* will be freed by Zend, thus don't use the mnd_ allocator */
  1201. *message = estrndup(stats_header->message, stats_header->message_len);
  1202. *message_len = stats_header->message_len;
  1203. PACKET_FREE(stats_header);
  1204. DBG_INF(*message);
  1205. DBG_RETURN(PASS);
  1206. }
  1207. /* }}} */
  1208. /* {{{ mysqlnd_conn::kill */
  1209. static enum_func_status
  1210. MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND * conn, unsigned int pid TSRMLS_DC)
  1211. {
  1212. enum_func_status ret;
  1213. char buff[4];
  1214. DBG_ENTER("mysqlnd_conn::kill");
  1215. DBG_INF_FMT("conn=%llu pid=%lu", conn->thread_id, pid);
  1216. int4store(buff, pid);
  1217. /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
  1218. if (pid != conn->thread_id) {
  1219. ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
  1220. /*
  1221. The server sends 0 but libmysql doesn't read it and has established
  1222. a protocol of giving back -1. Thus we have to follow it :(
  1223. */
  1224. SET_ERROR_AFF_ROWS(conn);
  1225. } else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE TSRMLS_CC))) {
  1226. CONN_SET_STATE(conn, CONN_QUIT_SENT);
  1227. }
  1228. DBG_RETURN(ret);
  1229. }
  1230. /* }}} */
  1231. /* {{{ mysqlnd_conn::set_charset */
  1232. static enum_func_status
  1233. MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * const csname TSRMLS_DC)
  1234. {
  1235. enum_func_status ret = PASS;
  1236. char * query;
  1237. size_t query_len;
  1238. const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
  1239. DBG_ENTER("mysqlnd_conn::set_charset");
  1240. DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
  1241. if (!charset) {
  1242. SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
  1243. "Invalid characterset or character set not supported");
  1244. DBG_RETURN(FAIL);
  1245. }
  1246. query_len = spprintf(&query, 0, "SET NAMES %s", csname);
  1247. if (FAIL == conn->m->query(conn, query, query_len TSRMLS_CC)) {
  1248. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error executing query");
  1249. } else if (conn->error_info.error_no) {
  1250. ret = FAIL;
  1251. } else {
  1252. conn->charset = charset;
  1253. }
  1254. efree(query); /* allocated by spprintf */
  1255. DBG_INF(ret == PASS? "PASS":"FAIL");
  1256. DBG_RETURN(ret);
  1257. }
  1258. /* }}} */
  1259. /* {{{ mysqlnd_conn::refresh */
  1260. static enum_func_status
  1261. MYSQLND_METHOD(mysqlnd_conn, refresh)(MYSQLND * const conn, uint8_t options TSRMLS_DC)
  1262. {
  1263. zend_uchar bits[1];
  1264. DBG_ENTER("mysqlnd_conn::refresh");
  1265. DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
  1266. int1store(bits, options);
  1267. DBG_RETURN(conn->m->simple_command(conn, COM_REFRESH, (char *)bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC));
  1268. }
  1269. /* }}} */
  1270. /* {{{ mysqlnd_conn::shutdown */
  1271. static enum_func_status
  1272. MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, uint8_t level TSRMLS_DC)
  1273. {
  1274. zend_uchar bits[1];
  1275. DBG_ENTER("mysqlnd_conn::shutdown");
  1276. DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
  1277. int1store(bits, level);
  1278. DBG_RETURN(conn->m->simple_command(conn, COM_SHUTDOWN, (char *)bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC));
  1279. }
  1280. /* }}} */
  1281. /* {{{ mysqlnd_send_close */
  1282. static enum_func_status
  1283. MYSQLND_METHOD(mysqlnd_conn, send_close)(MYSQLND * const conn TSRMLS_DC)
  1284. {
  1285. enum_func_status ret = PASS;
  1286. DBG_ENTER("mysqlnd_send_close");
  1287. DBG_INF_FMT("conn=%llu conn->net->stream->abstract=%p",
  1288. conn->thread_id, conn->net->stream? conn->net->stream->abstract:NULL);
  1289. switch (CONN_GET_STATE(conn)) {
  1290. case CONN_READY:
  1291. DBG_INF("Connection clean, sending COM_QUIT");
  1292. if (conn->net->stream) {
  1293. ret = conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE TSRMLS_CC);
  1294. }
  1295. /* Do nothing */
  1296. break;
  1297. case CONN_SENDING_LOAD_DATA:
  1298. /*
  1299. Don't send COM_QUIT if we are in a middle of a LOAD DATA or we
  1300. will crash (assert) a debug server.
  1301. */
  1302. case CONN_NEXT_RESULT_PENDING:
  1303. case CONN_QUERY_SENT:
  1304. case CONN_FETCHING_DATA:
  1305. MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
  1306. DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
  1307. /*
  1308. Do nothing, the connection will be brutally closed
  1309. and the server will catch it and free close from its side.
  1310. */
  1311. case CONN_ALLOCED:
  1312. /*
  1313. Allocated but not connected or there was failure when trying
  1314. to connect with pre-allocated connect.
  1315. Fall-through
  1316. */
  1317. case CONN_QUIT_SENT:
  1318. /* The user has killed its own connection */
  1319. break;
  1320. }
  1321. /*
  1322. We hold one reference, and every other object which needs the
  1323. connection does increase it by 1.
  1324. */
  1325. CONN_SET_STATE(conn, CONN_QUIT_SENT);
  1326. DBG_RETURN(ret);
  1327. }
  1328. /* }}} */
  1329. /* {{{ mysqlnd_conn::close */
  1330. static enum_func_status
  1331. MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type close_type TSRMLS_DC)
  1332. {
  1333. enum_func_status ret = PASS;
  1334. static enum_mysqlnd_collected_stats
  1335. close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
  1336. STAT_CLOSE_EXPLICIT,
  1337. STAT_CLOSE_IMPLICIT,
  1338. STAT_CLOSE_DISCONNECT
  1339. };
  1340. enum_mysqlnd_collected_stats statistic = close_type_to_stat_map[close_type];
  1341. DBG_ENTER("mysqlnd_conn::close");
  1342. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1343. if (conn->state >= CONN_READY) {
  1344. MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
  1345. MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
  1346. if (conn->persistent) {
  1347. MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
  1348. }
  1349. }
  1350. /*
  1351. Close now, free_reference will try,
  1352. if we are last, but that's not a problem.
  1353. */
  1354. ret = conn->m->send_close(conn TSRMLS_CC);
  1355. ret = conn->m->free_reference(conn TSRMLS_CC);
  1356. DBG_RETURN(ret);
  1357. }
  1358. /* }}} */
  1359. /* {{{ mysqlnd_conn::get_reference */
  1360. static MYSQLND *
  1361. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn TSRMLS_DC)
  1362. {
  1363. DBG_ENTER("mysqlnd_conn::get_reference");
  1364. ++conn->refcount;
  1365. DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
  1366. DBG_RETURN(conn);
  1367. }
  1368. /* }}} */
  1369. /* {{{ mysqlnd_conn::free_reference */
  1370. static enum_func_status
  1371. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS_DC)
  1372. {
  1373. enum_func_status ret = PASS;
  1374. DBG_ENTER("mysqlnd_conn::free_reference");
  1375. DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
  1376. if (!(--conn->refcount)) {
  1377. /*
  1378. No multithreading issues as we don't share the connection :)
  1379. This will free the object too, of course because references has
  1380. reached zero.
  1381. */
  1382. ret = conn->m->send_close(conn TSRMLS_CC);
  1383. conn->m->dtor(conn TSRMLS_CC);
  1384. }
  1385. DBG_RETURN(ret);
  1386. }
  1387. /* }}} */
  1388. /* {{{ mysqlnd_conn::get_state */
  1389. static enum mysqlnd_connection_state
  1390. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state)(MYSQLND * const conn TSRMLS_DC)
  1391. {
  1392. DBG_ENTER("mysqlnd_conn::get_state");
  1393. DBG_RETURN(conn->state);
  1394. }
  1395. /* }}} */
  1396. /* {{{ mysqlnd_conn::set_state */
  1397. static void
  1398. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC)
  1399. {
  1400. DBG_ENTER("mysqlnd_conn::set_state");
  1401. DBG_INF_FMT("New state=%u", new_state);
  1402. conn->state = new_state;
  1403. DBG_VOID_RETURN;
  1404. }
  1405. /* }}} */
  1406. /* {{{ mysqlnd_conn::field_count */
  1407. static unsigned int
  1408. MYSQLND_METHOD(mysqlnd_conn, field_count)(const MYSQLND * const conn TSRMLS_DC)
  1409. {
  1410. return conn->field_count;
  1411. }
  1412. /* }}} */
  1413. /* {{{ mysqlnd_conn::insert_id */
  1414. static uint64_t
  1415. MYSQLND_METHOD(mysqlnd_conn, insert_id)(const MYSQLND * const conn TSRMLS_DC)
  1416. {
  1417. return conn->upsert_status.last_insert_id;
  1418. }
  1419. /* }}} */
  1420. /* {{{ mysqlnd_conn::affected_rows */
  1421. static uint64_t
  1422. MYSQLND_METHOD(mysqlnd_conn, affected_rows)(const MYSQLND * const conn TSRMLS_DC)
  1423. {
  1424. return conn->upsert_status.affected_rows;
  1425. }
  1426. /* }}} */
  1427. /* {{{ mysqlnd_conn::matched_rows */
  1428. static uint64_t
  1429. MYSQLND_METHOD(mysqlnd_conn, matched_rows)(const MYSQLND * const conn)
  1430. {
  1431. char *p = conn->last_message;
  1432. if (p && strlen(p) >= 40) {
  1433. /* Read upsert result */
  1434. return (uint64_t) strtol(p + sizeof("Rows matched:"), NULL, 10);
  1435. }
  1436. /* fallback for selects */
  1437. return conn->upsert_status.affected_rows;
  1438. }
  1439. /* }}} */
  1440. /* {{{ mysqlnd_conn::warning_count */
  1441. static unsigned int
  1442. MYSQLND_METHOD(mysqlnd_conn, warning_count)(const MYSQLND * const conn TSRMLS_DC)
  1443. {
  1444. return conn->upsert_status.warning_count;
  1445. }
  1446. /* }}} */
  1447. /* {{{ mysqlnd_conn::info */
  1448. static const char *
  1449. MYSQLND_METHOD(mysqlnd_conn, info)(const MYSQLND * const conn TSRMLS_DC)
  1450. {
  1451. return conn->last_message;
  1452. }
  1453. /* }}} */
  1454. #if !defined(MYSQLND_USE_OPTIMISATIONS) || MYSQLND_USE_OPTIMISATIONS == 0
  1455. /* {{{ mysqlnd_get_client_info */
  1456. PHPAPI const char * mysqlnd_get_client_info()
  1457. {
  1458. return MYSQLND_VERSION;
  1459. }
  1460. /* }}} */
  1461. /* {{{ mysqlnd_get_client_version */
  1462. PHPAPI unsigned int mysqlnd_get_client_version()
  1463. {
  1464. return MYSQLND_VERSION_ID;
  1465. }
  1466. /* }}} */
  1467. #endif
  1468. /* {{{ mysqlnd_conn::get_server_info */
  1469. static const char *
  1470. MYSQLND_METHOD(mysqlnd_conn, get_server_info)(const MYSQLND * const conn TSRMLS_DC)
  1471. {
  1472. return conn->server_version;
  1473. }
  1474. /* }}} */
  1475. /* {{{ mysqlnd_conn::get_host_info */
  1476. static const char *
  1477. MYSQLND_METHOD(mysqlnd_conn, get_host_info)(const MYSQLND * const conn TSRMLS_DC)
  1478. {
  1479. return conn->host_info;
  1480. }
  1481. /* }}} */
  1482. /* {{{ mysqlnd_conn::get_proto_info */
  1483. static unsigned int
  1484. MYSQLND_METHOD(mysqlnd_conn, get_proto_info)(const MYSQLND *const conn TSRMLS_DC)
  1485. {
  1486. return conn->protocol_version;
  1487. }
  1488. /* }}} */
  1489. /* {{{ mysqlnd_conn::charset_name */
  1490. static const char *
  1491. MYSQLND_METHOD(mysqlnd_conn, charset_name)(const MYSQLND * const conn TSRMLS_DC)
  1492. {
  1493. return conn->charset->name;
  1494. }
  1495. /* }}} */
  1496. /* {{{ mysqlnd_conn::thread_id */
  1497. static uint64_t
  1498. MYSQLND_METHOD(mysqlnd_conn, thread_id)(const MYSQLND * const conn TSRMLS_DC)
  1499. {
  1500. return conn->thread_id;
  1501. }
  1502. /* }}} */
  1503. /* {{{ mysqlnd_conn::get_server_version */
  1504. static unsigned long
  1505. MYSQLND_METHOD(mysqlnd_conn, get_server_version)(const MYSQLND * const conn TSRMLS_DC)
  1506. {
  1507. long major, minor, patch;
  1508. char *p;
  1509. if (!(p = conn->server_version)) {
  1510. return 0;
  1511. }
  1512. major = strtol(p, &p, 10);
  1513. p += 1; /* consume the dot */
  1514. minor = strtol(p, &p, 10);
  1515. p += 1; /* consume the dot */
  1516. patch = strtol(p, &p, 10);
  1517. return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch));
  1518. }
  1519. /* }}} */
  1520. /* {{{ mysqlnd_conn::more_results */
  1521. static zend_bool
  1522. MYSQLND_METHOD(mysqlnd_conn, more_results)(const MYSQLND * const conn TSRMLS_DC)
  1523. {
  1524. DBG_ENTER("mysqlnd_conn::more_results");
  1525. /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
  1526. DBG_RETURN(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
  1527. }
  1528. /* }}} */
  1529. /* {{{ mysqlnd_conn::next_result */
  1530. static enum_func_status
  1531. MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC)
  1532. {
  1533. enum_func_status ret;
  1534. DBG_ENTER("mysqlnd_conn::next_result");
  1535. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1536. if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
  1537. DBG_RETURN(FAIL);
  1538. }
  1539. SET_EMPTY_ERROR(conn->error_info);
  1540. SET_ERROR_AFF_ROWS(conn);
  1541. /*
  1542. We are sure that there is a result set, since conn->state is set accordingly
  1543. in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
  1544. */
  1545. if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC))) {
  1546. /*
  1547. There can be an error in the middle of a multi-statement, which will cancel the multi-statement.
  1548. So there are no more results and we should just return FALSE, error_no has been set
  1549. */
  1550. if (!conn->error_info.error_no) {
  1551. DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
  1552. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid());
  1553. CONN_SET_STATE(conn, CONN_QUIT_SENT);
  1554. } else {
  1555. DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info.error_no, conn->error_info.error);
  1556. }
  1557. }
  1558. if (ret == PASS && conn->last_query_type == QUERY_UPSERT && conn->upsert_status.affected_rows) {
  1559. MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status.affected_rows);
  1560. }
  1561. DBG_RETURN(ret);
  1562. }
  1563. /* }}} */
  1564. /* {{{ mysqlnd_field_type_name */
  1565. PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
  1566. {
  1567. switch(field_type) {
  1568. case FIELD_TYPE_STRING:
  1569. case FIELD_TYPE_VAR_STRING:
  1570. return "string";
  1571. case FIELD_TYPE_TINY:
  1572. case FIELD_TYPE_SHORT:
  1573. case FIELD_TYPE_LONG:
  1574. case FIELD_TYPE_LONGLONG:
  1575. case FIELD_TYPE_INT24:
  1576. return "int";
  1577. case FIELD_TYPE_FLOAT:
  1578. case FIELD_TYPE_DOUBLE:
  1579. case FIELD_TYPE_DECIMAL:
  1580. case FIELD_TYPE_NEWDECIMAL:
  1581. return "real";
  1582. case FIELD_TYPE_TIMESTAMP:
  1583. return "timestamp";
  1584. case FIELD_TYPE_YEAR:
  1585. return "year";
  1586. case FIELD_TYPE_DATE:
  1587. case FIELD_TYPE_NEWDATE:
  1588. return "date";
  1589. case FIELD_TYPE_TIME:
  1590. return "time";
  1591. case FIELD_TYPE_SET:
  1592. return "set";
  1593. case FIELD_TYPE_ENUM:
  1594. return "enum";
  1595. case FIELD_TYPE_GEOMETRY:
  1596. return "geometry";
  1597. case FIELD_TYPE_DATETIME:
  1598. return "datetime";
  1599. case FIELD_TYPE_TINY_BLOB:
  1600. case FIELD_TYPE_MEDIUM_BLOB:
  1601. case FIELD_TYPE_LONG_BLOB:
  1602. case FIELD_TYPE_BLOB:
  1603. return "blob";
  1604. case FIELD_TYPE_NULL:
  1605. return "null";
  1606. case FIELD_TYPE_BIT:
  1607. return "bit";
  1608. default:
  1609. return "unknown";
  1610. }
  1611. }
  1612. /* }}} */
  1613. /* {{{ mysqlnd_conn::change_user */
  1614. static enum_func_status
  1615. MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn,
  1616. const char *user,
  1617. const char *passwd,
  1618. const char *db,
  1619. zend_bool silent TSRMLS_DC)
  1620. {
  1621. /*
  1622. User could be max 16 * 3 (utf8), pass is 20 usually, db is up to 64*3
  1623. Stack space is not that expensive, so use a bit more to be protected against
  1624. buffer overflows.
  1625. */
  1626. size_t user_len;
  1627. enum_func_status ret = FAIL;
  1628. MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp;
  1629. char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 2 /* charset*/ ];
  1630. char *p = buffer;
  1631. const MYSQLND_CHARSET * old_cs = conn->charset;
  1632. DBG_ENTER("mysqlnd_conn::change_user");
  1633. DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
  1634. conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 );
  1635. SET_ERROR_AFF_ROWS(conn);
  1636. if (!user) {
  1637. user = "";
  1638. }
  1639. if (!passwd) {
  1640. passwd = "";
  1641. }
  1642. if (!db) {
  1643. db = "";
  1644. }
  1645. /* 1. user ASCIIZ */
  1646. user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_USER_LEN);
  1647. memcpy(p, user, user_len);
  1648. p += user_len;
  1649. *p++ = '\0';
  1650. /* 2. password SCRAMBLE_LENGTH followed by the scramble or \0 */
  1651. if (passwd[0]) {
  1652. *p++ = SCRAMBLE_LENGTH;
  1653. php_mysqlnd_scramble((unsigned char *)p, conn->scramble, (unsigned char *)passwd);
  1654. p += SCRAMBLE_LENGTH;
  1655. } else {
  1656. *p++ = '\0';
  1657. }
  1658. /* 3. db ASCIIZ */
  1659. if (db[0]) {
  1660. size_t db_len = MIN(strlen(db), MYSQLND_MAX_ALLOWED_DB_LEN);
  1661. memcpy(p, db, db_len);
  1662. p += db_len;
  1663. }
  1664. *p++ = '\0';
  1665. /*
  1666. 4. request the current charset, or it will be reset to the system one.
  1667. 5.0 doesn't support it. Support added in 5.1.23 by fixing the following bug :
  1668. Bug #30472 libmysql doesn't reset charset, insert_id after succ. mysql_change_user() call
  1669. */
  1670. if (mysqlnd_get_server_version(conn) >= 50123) {
  1671. int2store(p, conn->charset->nr);
  1672. p+=2;
  1673. }
  1674. if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer, p - buffer,
  1675. PROT_LAST /* we will handle the OK packet*/,
  1676. silent, TRUE TSRMLS_CC)) {
  1677. DBG_RETURN(FAIL);
  1678. }
  1679. chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC);
  1680. if (!chg_user_resp) {
  1681. SET_OOM_ERROR(conn->error_info);
  1682. goto end;
  1683. }
  1684. ret = PACKET_READ(chg_user_resp, conn);
  1685. conn->error_info = chg_user_resp->error_info;
  1686. if (conn->error_info.error_no) {
  1687. ret = FAIL;
  1688. /*
  1689. COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
  1690. bug#25371 mysql_change_user() triggers "packets out of sync"
  1691. When it gets fixed, there should be one more check here
  1692. */
  1693. if (mysqlnd_get_server_version(conn) > 50113L && mysqlnd_get_server_version(conn) < 50118L) {
  1694. MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
  1695. if (redundant_error_packet) {
  1696. PACKET_READ(redundant_error_packet, conn);
  1697. PACKET_FREE(redundant_error_packet);
  1698. DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", mysqlnd_get_server_version(conn));
  1699. } else {
  1700. SET_OOM_ERROR(conn->error_info);
  1701. }
  1702. }
  1703. }
  1704. if (ret == PASS) {
  1705. char * tmp = NULL;
  1706. /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
  1707. tmp = mnd_pestrndup(user, user_len, conn->persistent);
  1708. if (conn->user) {
  1709. mnd_pefree(conn->user, conn->persistent);
  1710. }
  1711. conn->user = tmp;
  1712. tmp = mnd_pestrdup(passwd, conn->persistent);
  1713. if (conn->passwd) {
  1714. mnd_pefree(conn->passwd, conn->persistent);
  1715. }
  1716. conn->passwd = tmp;
  1717. if (conn->last_message) {
  1718. mnd_pefree(conn->last_message, conn->persistent);
  1719. conn->last_message = NULL;
  1720. }
  1721. memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
  1722. /* set charset for old servers */
  1723. if (mysqlnd_get_server_version(conn) < 50123) {
  1724. ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC);
  1725. }
  1726. } else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
  1727. /* old authentication with new server !*/
  1728. DBG_ERR(mysqlnd_old_passwd);
  1729. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
  1730. }
  1731. end:
  1732. PACKET_FREE(chg_user_resp);
  1733. /*
  1734. Here we should close all statements. Unbuffered queries should not be a
  1735. problem as we won't allow sending COM_CHANGE_USER.
  1736. */
  1737. DBG_INF(ret == PASS? "PASS":"FAIL");
  1738. DBG_RETURN(ret);
  1739. }
  1740. /* }}} */
  1741. /* {{{ mysqlnd_conn::set_client_option */
  1742. static enum_func_status
  1743. MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn,
  1744. enum mysqlnd_option option,
  1745. const char * const value
  1746. TSRMLS_DC)
  1747. {
  1748. enum_func_status ret = PASS;
  1749. DBG_ENTER("mysqlnd_conn::set_client_option");
  1750. DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
  1751. switch (option) {
  1752. case MYSQL_OPT_COMPRESS:
  1753. #ifdef WHEN_SUPPORTED_BY_MYSQLI
  1754. case MYSQL_OPT_READ_TIMEOUT:
  1755. case MYSQL_OPT_WRITE_TIMEOUT:
  1756. #endif
  1757. case MYSQLND_OPT_SSL_KEY:
  1758. case MYSQLND_OPT_SSL_CERT:
  1759. case MYSQLND_OPT_SSL_CA:
  1760. case MYSQLND_OPT_SSL_CAPATH:
  1761. case MYSQLND_OPT_SSL_CIPHER:
  1762. case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
  1763. case MYSQL_OPT_CONNECT_TIMEOUT:
  1764. case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
  1765. case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
  1766. ret = conn->net->m.set_client_option(conn->net, option, value TSRMLS_CC);
  1767. break;
  1768. #if MYSQLND_UNICODE
  1769. case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE:
  1770. conn->options.numeric_and_datetime_as_unicode = *(unsigned int*) value;
  1771. break;
  1772. #endif
  1773. #ifdef MYSQLND_STRING_TO_INT_CONVERSION
  1774. case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
  1775. DBG_INF("MYSQLND_OPT_INT_AND_FLOAT_NATIVE");
  1776. conn->options.int_and_float_native = *(unsigned int*) value;
  1777. break;
  1778. #endif
  1779. case MYSQL_OPT_LOCAL_INFILE:
  1780. DBG_INF("MYSQL_OPT_LOCAL_INFILE");
  1781. if (!value || (*(unsigned int*) value) ? 1 : 0) {
  1782. conn->options.flags |= CLIENT_LOCAL_FILES;
  1783. } else {
  1784. conn->options.flags &= ~CLIENT_LOCAL_FILES;
  1785. }
  1786. break;
  1787. case MYSQL_INIT_COMMAND:
  1788. {
  1789. char ** new_init_commands;
  1790. char * new_command;
  1791. DBG_INF("MYSQL_INIT_COMMAND");
  1792. DBG_INF_FMT("command=%s", value);
  1793. /* when num_commands is 0, then realloc will be effectively a malloc call, internally */
  1794. /* Don't assign to conn->options.init_commands because in case of OOM we will lose the pointer and leak */
  1795. new_init_commands = mnd_perealloc(conn->options.init_commands, sizeof(char *) * (conn->options.num_commands + 1), conn->persistent);
  1796. if (!new_init_commands) {
  1797. goto oom;
  1798. }
  1799. conn->options.init_commands = new_init_commands;
  1800. new_command = mnd_pestrdup(value, conn->persistent);
  1801. if (!new_command) {
  1802. goto oom;
  1803. }
  1804. conn->options.init_commands[conn->options.num_commands] = new_command;
  1805. ++conn->options.num_commands;
  1806. break;
  1807. }
  1808. case MYSQL_READ_DEFAULT_FILE:
  1809. case MYSQL_READ_DEFAULT_GROUP:
  1810. #ifdef WHEN_SUPPORTED_BY_MYSQLI
  1811. case MYSQL_SET_CLIENT_IP:
  1812. case MYSQL_REPORT_DATA_TRUNCATION:
  1813. #endif
  1814. /* currently not supported. Todo!! */
  1815. break;
  1816. case MYSQL_SET_CHARSET_NAME:
  1817. {
  1818. char * new_charset_name = mnd_pestrdup(value, conn->persistent);
  1819. DBG_INF("MYSQL_SET_CHARSET_NAME");
  1820. if (!new_charset_name) {
  1821. goto oom;
  1822. }
  1823. if (conn->options.charset_name) {
  1824. mnd_pefree(conn->options.charset_name, conn->persistent);
  1825. }
  1826. conn->options.charset_name = new_charset_name;
  1827. DBG_INF_FMT("charset=%s", conn->options.charset_name);
  1828. break;
  1829. }
  1830. case MYSQL_OPT_NAMED_PIPE:
  1831. conn->options.protocol = MYSQL_PROTOCOL_PIPE;
  1832. break;
  1833. case MYSQL_OPT_PROTOCOL:
  1834. if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
  1835. conn->options.protocol = *(unsigned int*) value;
  1836. }
  1837. break;
  1838. #ifdef WHEN_SUPPORTED_BY_MYSQLI
  1839. case MYSQL_SET_CHARSET_DIR:
  1840. case MYSQL_OPT_RECONNECT:
  1841. /* we don't need external character sets, all character sets are
  1842. compiled in. For compatibility we just ignore this setting.
  1843. Same for protocol, we don't support old protocol */
  1844. case MYSQL_OPT_USE_REMOTE_CONNECTION:
  1845. case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
  1846. case MYSQL_OPT_GUESS_CONNECTION:
  1847. /* todo: throw an error, we don't support embedded */
  1848. break;
  1849. #endif
  1850. #ifdef WHEN_SUPPORTED_BY_MYSQLI
  1851. case MYSQL_SHARED_MEMORY_BASE_NAME:
  1852. case MYSQL_OPT_USE_RESULT:
  1853. case MYSQL_SECURE_AUTH:
  1854. /* not sure, todo ? */
  1855. #endif
  1856. default:
  1857. ret = FAIL;
  1858. }
  1859. DBG_RETURN(ret);
  1860. oom:
  1861. SET_OOM_ERROR(conn->error_info);
  1862. DBG_RETURN(FAIL);
  1863. }
  1864. /* }}} */
  1865. /* {{{ mysqlnd_conn::use_result */
  1866. static MYSQLND_RES *
  1867. MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC)
  1868. {
  1869. MYSQLND_RES * result;
  1870. DBG_ENTER("mysqlnd_conn::use_result");
  1871. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1872. if (!conn->current_result) {
  1873. DBG_RETURN(NULL);
  1874. }
  1875. /* Nothing to store for UPSERT/LOAD DATA */
  1876. if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
  1877. SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
  1878. mysqlnd_out_of_sync);
  1879. DBG_ERR("Command out of sync");
  1880. DBG_RETURN(NULL);
  1881. }
  1882. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
  1883. conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC);
  1884. result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC);
  1885. if (!result) {
  1886. conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
  1887. }
  1888. conn->current_result = NULL;
  1889. DBG_RETURN(result);
  1890. }
  1891. /* }}} */
  1892. /* {{{ mysqlnd_conn::store_result */
  1893. static MYSQLND_RES *
  1894. MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC)
  1895. {
  1896. MYSQLND_RES *result;
  1897. DBG_ENTER("mysqlnd_conn::store_result");
  1898. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1899. if (!conn->current_result) {
  1900. DBG_RETURN(NULL);
  1901. }
  1902. /* Nothing to store for UPSERT/LOAD DATA*/
  1903. if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
  1904. SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
  1905. mysqlnd_out_of_sync);
  1906. DBG_ERR("Command out of sync");
  1907. DBG_RETURN(NULL);
  1908. }
  1909. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
  1910. result = conn->current_result->m.store_result(conn->current_result, conn, FALSE TSRMLS_CC);
  1911. if (!result) {
  1912. conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
  1913. }
  1914. conn->current_result = NULL;
  1915. DBG_RETURN(result);
  1916. }
  1917. /* }}} */
  1918. /* {{{ mysqlnd_conn::get_connection_stats */
  1919. static void
  1920. MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn,
  1921. zval *return_value
  1922. TSRMLS_DC ZEND_FILE_LINE_DC)
  1923. {
  1924. DBG_ENTER("mysqlnd_conn::get_connection_stats");
  1925. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1926. mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value TSRMLS_CC ZEND_FILE_LINE_CC);
  1927. DBG_VOID_RETURN;
  1928. }
  1929. /* }}} */
  1930. /* {{{ mysqlnd_conn::set_autocommit */
  1931. static enum_func_status
  1932. MYSQLND_METHOD(mysqlnd_conn, set_autocommit)(MYSQLND * conn, unsigned int mode TSRMLS_DC)
  1933. {
  1934. enum_func_status ret;
  1935. DBG_ENTER("mysqlnd_conn::set_autocommit");
  1936. ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1 TSRMLS_CC);
  1937. DBG_RETURN(ret);
  1938. }
  1939. /* }}} */
  1940. /* {{{ mysqlnd_conn::tx_commit */
  1941. static enum_func_status
  1942. MYSQLND_METHOD(mysqlnd_conn, tx_commit)(MYSQLND * conn TSRMLS_DC)
  1943. {
  1944. enum_func_status ret;
  1945. DBG_ENTER("mysqlnd_conn::tx_commit");
  1946. ret = conn->m->query(conn, "COMMIT", sizeof("COMMIT") - 1 TSRMLS_CC);
  1947. DBG_RETURN(ret);
  1948. }
  1949. /* }}} */
  1950. /* {{{ mysqlnd_conn::tx_rollback */
  1951. static enum_func_status
  1952. MYSQLND_METHOD(mysqlnd_conn, tx_rollback)(MYSQLND * conn TSRMLS_DC)
  1953. {
  1954. enum_func_status ret;
  1955. DBG_ENTER("mysqlnd_conn::tx_rollback");
  1956. ret = conn->m->query(conn, "ROLLBACK", sizeof("ROLLBACK") - 1 TSRMLS_CC);
  1957. DBG_RETURN(ret);
  1958. }
  1959. /* }}} */
  1960. MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC);
  1961. static enum_func_status MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC);
  1962. static
  1963. MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
  1964. MYSQLND_METHOD(mysqlnd_conn, init),
  1965. MYSQLND_METHOD(mysqlnd_conn, connect),
  1966. MYSQLND_METHOD(mysqlnd_conn, escape_string),
  1967. MYSQLND_METHOD(mysqlnd_conn, set_charset),
  1968. MYSQLND_METHOD(mysqlnd_conn, query),
  1969. MYSQLND_METHOD(mysqlnd_conn, send_query),
  1970. MYSQLND_METHOD(mysqlnd_conn, reap_query),
  1971. MYSQLND_METHOD(mysqlnd_conn, use_result),
  1972. MYSQLND_METHOD(mysqlnd_conn, store_result),
  1973. MYSQLND_METHOD(mysqlnd_conn, next_result),
  1974. MYSQLND_METHOD(mysqlnd_conn, more_results),
  1975. _mysqlnd_stmt_init,
  1976. MYSQLND_METHOD(mysqlnd_conn, shutdown),
  1977. MYSQLND_METHOD(mysqlnd_conn, refresh),
  1978. MYSQLND_METHOD(mysqlnd_conn, ping),
  1979. MYSQLND_METHOD(mysqlnd_conn, kill),
  1980. MYSQLND_METHOD(mysqlnd_conn, select_db),
  1981. MYSQLND_METHOD(mysqlnd_conn, dump_debug_info),
  1982. MYSQLND_METHOD(mysqlnd_conn, change_user),
  1983. MYSQLND_METHOD(mysqlnd_conn, errno),
  1984. MYSQLND_METHOD(mysqlnd_conn, error),
  1985. MYSQLND_METHOD(mysqlnd_conn, sqlstate),
  1986. MYSQLND_METHOD(mysqlnd_conn, thread_id),
  1987. MYSQLND_METHOD(mysqlnd_conn, get_connection_stats),
  1988. MYSQLND_METHOD(mysqlnd_conn, get_server_version),
  1989. MYSQLND_METHOD(mysqlnd_conn, get_server_info),
  1990. MYSQLND_METHOD(mysqlnd_conn, statistic),
  1991. MYSQLND_METHOD(mysqlnd_conn, get_host_info),
  1992. MYSQLND_METHOD(mysqlnd_conn, get_proto_info),
  1993. MYSQLND_METHOD(mysqlnd_conn, info),
  1994. MYSQLND_METHOD(mysqlnd_conn, charset_name),
  1995. MYSQLND_METHOD(mysqlnd_conn, list_fields),
  1996. MYSQLND_METHOD(mysqlnd_conn, list_method),
  1997. MYSQLND_METHOD(mysqlnd_conn, insert_id),
  1998. MYSQLND_METHOD(mysqlnd_conn, affected_rows),
  1999. MYSQLND_METHOD(mysqlnd_conn, matched_rows),
  2000. MYSQLND_METHOD(mysqlnd_conn, warning_count),
  2001. MYSQLND_METHOD(mysqlnd_conn, field_count),
  2002. MYSQLND_METHOD(mysqlnd_conn, set_server_option),
  2003. MYSQLND_METHOD(mysqlnd_conn, set_client_option),
  2004. MYSQLND_METHOD(mysqlnd_conn, free_contents),
  2005. MYSQLND_METHOD(mysqlnd_conn, free_options),
  2006. MYSQLND_METHOD(mysqlnd_conn, close),
  2007. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
  2008. mysqlnd_query_read_result_set_header,
  2009. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference),
  2010. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference),
  2011. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state),
  2012. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state),
  2013. MYSQLND_METHOD(mysqlnd_conn, simple_command),
  2014. MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response),
  2015. MYSQLND_METHOD(mysqlnd_conn, restart_psession),
  2016. MYSQLND_METHOD(mysqlnd_conn, end_psession),
  2017. MYSQLND_METHOD(mysqlnd_conn, send_close),
  2018. MYSQLND_METHOD(mysqlnd_conn, ssl_set),
  2019. mysqlnd_result_init
  2020. #ifdef AUTOCOMMIT_TX_COMMIT_ROLLBACK
  2021. ,MYSQLND_METHOD(mysqlnd_conn, set_autocommit),
  2022. MYSQLND_METHOD(mysqlnd_conn, tx_commit),
  2023. MYSQLND_METHOD(mysqlnd_conn, tx_rollback)
  2024. #endif
  2025. MYSQLND_CLASS_METHODS_END;
  2026. /* {{{ mysqlnd_conn::init */
  2027. static enum_func_status
  2028. MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC)
  2029. {
  2030. DBG_ENTER("mysqlnd_conn::init");
  2031. mysqlnd_stats_init(&conn->stats, STAT_LAST);
  2032. SET_ERROR_AFF_ROWS(conn);
  2033. conn->net = mysqlnd_net_init(conn->persistent TSRMLS_CC);
  2034. conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC);
  2035. DBG_RETURN(conn->net && conn->protocol? PASS:FAIL);
  2036. }
  2037. /* }}} */
  2038. /* {{{ mysqlnd_init */
  2039. PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC)
  2040. {
  2041. size_t alloc_size = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
  2042. MYSQLND *ret;
  2043. DBG_ENTER("mysqlnd_init");
  2044. DBG_INF_FMT("persistent=%u", persistent);
  2045. ret = mnd_pecalloc(1, alloc_size, persistent);
  2046. if (!ret) {
  2047. DBG_RETURN(NULL);
  2048. }
  2049. ret->persistent = persistent;
  2050. ret->m = mysqlnd_conn_methods;
  2051. CONN_SET_STATE(ret, CONN_ALLOCED);
  2052. ret->m->get_reference(ret TSRMLS_CC);
  2053. if (PASS != ret->m->init(ret TSRMLS_CC)) {
  2054. ret->m->dtor(ret TSRMLS_CC);
  2055. ret = NULL;
  2056. }
  2057. DBG_RETURN(ret);
  2058. }
  2059. /* }}} */
  2060. /* {{{ mysqlnd_library_init */
  2061. PHPAPI void mysqlnd_library_init(TSRMLS_D)
  2062. {
  2063. if (mysqlnd_library_initted == FALSE) {
  2064. mysqlnd_library_initted = TRUE;
  2065. mysqlnd_conn_methods = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_conn);
  2066. _mysqlnd_init_ps_subsystem();
  2067. /* Should be calloc, as mnd_calloc will reference LOCK_access*/
  2068. mysqlnd_stats_init(&mysqlnd_global_stats, STAT_LAST);
  2069. }
  2070. }
  2071. /* }}} */
  2072. /* {{{ mysqlnd_conn_get_methods */
  2073. PHPAPI struct st_mysqlnd_conn_methods * mysqlnd_conn_get_methods()
  2074. {
  2075. return mysqlnd_conn_methods;
  2076. }
  2077. /* }}} */
  2078. /* {{{ mysqlnd_conn_set_methods */
  2079. PHPAPI void mysqlnd_conn_set_methods(struct st_mysqlnd_conn_methods *methods)
  2080. {
  2081. mysqlnd_conn_methods = methods;
  2082. }
  2083. /* }}} */
  2084. static unsigned int mysqlnd_plugins_counter = 0;
  2085. /* {{{ mysqlnd_plugin_register */
  2086. PHPAPI unsigned int mysqlnd_plugin_register()
  2087. {
  2088. return mysqlnd_plugins_counter++;
  2089. }
  2090. /* }}} */
  2091. /* {{{ mysqlnd_plugin_count */
  2092. PHPAPI unsigned int mysqlnd_plugin_count()
  2093. {
  2094. return mysqlnd_plugins_counter;
  2095. }
  2096. /* }}} */
  2097. /* {{{ _mysqlnd_plugin_get_plugin_connection_data */
  2098. PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int plugin_id TSRMLS_DC)
  2099. {
  2100. DBG_ENTER("_mysqlnd_plugin_get_plugin_connection_data");
  2101. DBG_INF_FMT("plugin_id=%u", plugin_id);
  2102. if (!conn || plugin_id >= mysqlnd_plugin_count()) {
  2103. return NULL;
  2104. }
  2105. DBG_RETURN((void *)((char *)conn + sizeof(MYSQLND) + plugin_id * sizeof(void *)));
  2106. }
  2107. /* }}} */
  2108. /*
  2109. * Local variables:
  2110. * tab-width: 4
  2111. * c-basic-offset: 4
  2112. * End:
  2113. * vim600: noet sw=4 ts=4 fdm=marker
  2114. * vim<600: noet sw=4 ts=4
  2115. */