mysql_driver.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2018 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. | Author: George Schlossnagle <george@omniti.com> |
  16. | Wez Furlong <wez@php.net> |
  17. | Johannes Schlueter <johannes@mysql.com> |
  18. +----------------------------------------------------------------------+
  19. */
  20. #ifdef HAVE_CONFIG_H
  21. #include "config.h"
  22. #endif
  23. #include "php.h"
  24. #include "php_ini.h"
  25. #include "ext/standard/info.h"
  26. #include "pdo/php_pdo.h"
  27. #include "pdo/php_pdo_driver.h"
  28. #include "php_pdo_mysql.h"
  29. #include "php_pdo_mysql_int.h"
  30. #ifndef PDO_USE_MYSQLND
  31. #include <mysqld_error.h>
  32. #endif
  33. #include "zend_exceptions.h"
  34. #if defined(PDO_USE_MYSQLND)
  35. # define pdo_mysql_init(persistent) mysqlnd_init(MYSQLND_CLIENT_NO_FLAG, persistent)
  36. #else
  37. # define pdo_mysql_init(persistent) mysql_init(NULL)
  38. #endif
  39. /* {{{ _pdo_mysql_error */
  40. int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line)
  41. {
  42. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  43. pdo_error_type *pdo_err;
  44. pdo_mysql_error_info *einfo;
  45. pdo_mysql_stmt *S = NULL;
  46. PDO_DBG_ENTER("_pdo_mysql_error");
  47. PDO_DBG_INF_FMT("file=%s line=%d", file, line);
  48. if (stmt) {
  49. S = (pdo_mysql_stmt*)stmt->driver_data;
  50. pdo_err = &stmt->error_code;
  51. einfo = &S->einfo;
  52. } else {
  53. pdo_err = &dbh->error_code;
  54. einfo = &H->einfo;
  55. }
  56. if (S && S->stmt) {
  57. einfo->errcode = mysql_stmt_errno(S->stmt);
  58. } else {
  59. einfo->errcode = mysql_errno(H->server);
  60. }
  61. einfo->file = file;
  62. einfo->line = line;
  63. if (einfo->errmsg) {
  64. pefree(einfo->errmsg, dbh->is_persistent);
  65. einfo->errmsg = NULL;
  66. }
  67. if (einfo->errcode) {
  68. if (einfo->errcode == 2014) {
  69. einfo->errmsg = pestrdup(
  70. "Cannot execute queries while other unbuffered queries are active. "
  71. "Consider using PDOStatement::fetchAll(). Alternatively, if your code "
  72. "is only ever going to run against mysql, you may enable query "
  73. "buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.",
  74. dbh->is_persistent);
  75. } else if (einfo->errcode == 2057) {
  76. einfo->errmsg = pestrdup(
  77. "A stored procedure returning result sets of different size was called. "
  78. "This is not supported by libmysql",
  79. dbh->is_persistent);
  80. } else {
  81. einfo->errmsg = pestrdup(mysql_error(H->server), dbh->is_persistent);
  82. }
  83. } else { /* no error */
  84. strcpy(*pdo_err, PDO_ERR_NONE);
  85. PDO_DBG_RETURN(0);
  86. }
  87. if (S && S->stmt) {
  88. strcpy(*pdo_err, mysql_stmt_sqlstate(S->stmt));
  89. } else {
  90. strcpy(*pdo_err, mysql_sqlstate(H->server));
  91. }
  92. if (!dbh->methods) {
  93. PDO_DBG_INF("Throwing exception");
  94. pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);
  95. }
  96. PDO_DBG_RETURN(einfo->errcode);
  97. }
  98. /* }}} */
  99. /* {{{ pdo_mysql_fetch_error_func */
  100. static int pdo_mysql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info)
  101. {
  102. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  103. pdo_mysql_error_info *einfo = &H->einfo;
  104. PDO_DBG_ENTER("pdo_mysql_fetch_error_func");
  105. PDO_DBG_INF_FMT("dbh=%p stmt=%p", dbh, stmt);
  106. if (stmt) {
  107. pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
  108. einfo = &S->einfo;
  109. } else {
  110. einfo = &H->einfo;
  111. }
  112. if (einfo->errcode) {
  113. add_next_index_long(info, einfo->errcode);
  114. add_next_index_string(info, einfo->errmsg);
  115. }
  116. PDO_DBG_RETURN(1);
  117. }
  118. /* }}} */
  119. /* {{{ mysql_handle_closer */
  120. static int mysql_handle_closer(pdo_dbh_t *dbh)
  121. {
  122. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  123. PDO_DBG_ENTER("mysql_handle_closer");
  124. PDO_DBG_INF_FMT("dbh=%p", dbh);
  125. if (H) {
  126. if (H->server) {
  127. mysql_close(H->server);
  128. }
  129. if (H->einfo.errmsg) {
  130. pefree(H->einfo.errmsg, dbh->is_persistent);
  131. }
  132. pefree(H, dbh->is_persistent);
  133. dbh->driver_data = NULL;
  134. }
  135. PDO_DBG_RETURN(0);
  136. }
  137. /* }}} */
  138. /* {{{ mysql_handle_preparer */
  139. static int mysql_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options)
  140. {
  141. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  142. pdo_mysql_stmt *S = ecalloc(1, sizeof(pdo_mysql_stmt));
  143. char *nsql = NULL;
  144. size_t nsql_len = 0;
  145. int ret;
  146. int server_version;
  147. PDO_DBG_ENTER("mysql_handle_preparer");
  148. PDO_DBG_INF_FMT("dbh=%p", dbh);
  149. PDO_DBG_INF_FMT("sql=%.*s", (int)sql_len, sql);
  150. S->H = H;
  151. stmt->driver_data = S;
  152. stmt->methods = &mysql_stmt_methods;
  153. if (H->emulate_prepare) {
  154. goto end;
  155. }
  156. server_version = mysql_get_server_version(H->server);
  157. if (server_version < 40100) {
  158. goto fallback;
  159. }
  160. stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
  161. ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len);
  162. if (ret == 1) {
  163. /* query was rewritten */
  164. sql = nsql;
  165. sql_len = nsql_len;
  166. } else if (ret == -1) {
  167. /* failed to parse */
  168. strcpy(dbh->error_code, stmt->error_code);
  169. PDO_DBG_RETURN(0);
  170. }
  171. if (!(S->stmt = mysql_stmt_init(H->server))) {
  172. pdo_mysql_error(dbh);
  173. if (nsql) {
  174. efree(nsql);
  175. }
  176. PDO_DBG_RETURN(0);
  177. }
  178. if (mysql_stmt_prepare(S->stmt, sql, sql_len)) {
  179. /* TODO: might need to pull statement specific info here? */
  180. /* if the query isn't supported by the protocol, fallback to emulation */
  181. if (mysql_errno(H->server) == 1295) {
  182. if (nsql) {
  183. efree(nsql);
  184. }
  185. goto fallback;
  186. }
  187. pdo_mysql_error(dbh);
  188. if (nsql) {
  189. efree(nsql);
  190. }
  191. PDO_DBG_RETURN(0);
  192. }
  193. if (nsql) {
  194. efree(nsql);
  195. }
  196. S->num_params = mysql_stmt_param_count(S->stmt);
  197. if (S->num_params) {
  198. S->params_given = 0;
  199. #if defined(PDO_USE_MYSQLND)
  200. S->params = NULL;
  201. #else
  202. S->params = ecalloc(S->num_params, sizeof(MYSQL_BIND));
  203. S->in_null = ecalloc(S->num_params, sizeof(my_bool));
  204. S->in_length = ecalloc(S->num_params, sizeof(zend_ulong));
  205. #endif
  206. }
  207. dbh->alloc_own_columns = 1;
  208. S->max_length = pdo_attr_lval(driver_options, PDO_ATTR_MAX_COLUMN_LEN, 0);
  209. PDO_DBG_RETURN(1);
  210. fallback:
  211. end:
  212. stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
  213. PDO_DBG_RETURN(1);
  214. }
  215. /* }}} */
  216. /* {{{ mysql_handle_doer */
  217. static zend_long mysql_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len)
  218. {
  219. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  220. PDO_DBG_ENTER("mysql_handle_doer");
  221. PDO_DBG_INF_FMT("dbh=%p", dbh);
  222. PDO_DBG_INF_FMT("sql=%.*s", (int)sql_len, sql);
  223. if (mysql_real_query(H->server, sql, sql_len)) {
  224. pdo_mysql_error(dbh);
  225. PDO_DBG_RETURN(-1);
  226. } else {
  227. my_ulonglong c = mysql_affected_rows(H->server);
  228. if (c == (my_ulonglong) -1) {
  229. pdo_mysql_error(dbh);
  230. PDO_DBG_RETURN(H->einfo.errcode ? -1 : 0);
  231. } else {
  232. /* MULTI_QUERY support - eat up all unfetched result sets */
  233. MYSQL_RES* result;
  234. while (mysql_more_results(H->server)) {
  235. if (mysql_next_result(H->server)) {
  236. PDO_DBG_RETURN(1);
  237. }
  238. result = mysql_store_result(H->server);
  239. if (result) {
  240. mysql_free_result(result);
  241. }
  242. }
  243. PDO_DBG_RETURN((int)c);
  244. }
  245. }
  246. }
  247. /* }}} */
  248. /* {{{ pdo_mysql_last_insert_id */
  249. static char *pdo_mysql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len)
  250. {
  251. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  252. char *id = php_pdo_int64_to_str(mysql_insert_id(H->server));
  253. PDO_DBG_ENTER("pdo_mysql_last_insert_id");
  254. *len = strlen(id);
  255. PDO_DBG_RETURN(id);
  256. }
  257. /* }}} */
  258. #if defined(PDO_USE_MYSQLND) || MYSQL_VERSION_ID < 50707 || defined(MARIADB_BASE_VERSION)
  259. # define mysql_real_escape_string_quote(mysql, to, from, length, quote) \
  260. mysql_real_escape_string(mysql, to, from, length)
  261. #endif
  262. /* {{{ mysql_handle_quoter */
  263. static int mysql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype )
  264. {
  265. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  266. zend_bool use_national_character_set = 0;
  267. if (H->assume_national_character_set_strings) {
  268. use_national_character_set = 1;
  269. }
  270. if ((paramtype & PDO_PARAM_STR_NATL) == PDO_PARAM_STR_NATL) {
  271. use_national_character_set = 1;
  272. }
  273. if ((paramtype & PDO_PARAM_STR_CHAR) == PDO_PARAM_STR_CHAR) {
  274. use_national_character_set = 0;
  275. }
  276. PDO_DBG_ENTER("mysql_handle_quoter");
  277. PDO_DBG_INF_FMT("dbh=%p", dbh);
  278. PDO_DBG_INF_FMT("unquoted=%.*s", (int)unquotedlen, unquoted);
  279. *quoted = safe_emalloc(2, unquotedlen, 3 + (use_national_character_set ? 1 : 0));
  280. if (use_national_character_set) {
  281. *quotedlen = mysql_real_escape_string_quote(H->server, *quoted + 2, unquoted, unquotedlen, '\'');
  282. (*quoted)[0] = 'N';
  283. (*quoted)[1] = '\'';
  284. ++*quotedlen; /* N prefix */
  285. } else {
  286. *quotedlen = mysql_real_escape_string_quote(H->server, *quoted + 1, unquoted, unquotedlen, '\'');
  287. (*quoted)[0] = '\'';
  288. }
  289. (*quoted)[++*quotedlen] = '\'';
  290. (*quoted)[++*quotedlen] = '\0';
  291. PDO_DBG_INF_FMT("quoted=%.*s", (int)*quotedlen, *quoted);
  292. PDO_DBG_RETURN(1);
  293. }
  294. /* }}} */
  295. /* {{{ mysql_handle_begin */
  296. static int mysql_handle_begin(pdo_dbh_t *dbh)
  297. {
  298. PDO_DBG_ENTER("mysql_handle_quoter");
  299. PDO_DBG_INF_FMT("dbh=%p", dbh);
  300. PDO_DBG_RETURN(0 <= mysql_handle_doer(dbh, ZEND_STRL("START TRANSACTION")));
  301. }
  302. /* }}} */
  303. /* {{{ mysql_handle_commit */
  304. static int mysql_handle_commit(pdo_dbh_t *dbh)
  305. {
  306. PDO_DBG_ENTER("mysql_handle_commit");
  307. PDO_DBG_INF_FMT("dbh=%p", dbh);
  308. PDO_DBG_RETURN(0 == mysql_commit(((pdo_mysql_db_handle *)dbh->driver_data)->server));
  309. }
  310. /* }}} */
  311. /* {{{ mysql_handle_rollback */
  312. static int mysql_handle_rollback(pdo_dbh_t *dbh)
  313. {
  314. PDO_DBG_ENTER("mysql_handle_rollback");
  315. PDO_DBG_INF_FMT("dbh=%p", dbh);
  316. PDO_DBG_RETURN(0 <= mysql_rollback(((pdo_mysql_db_handle *)dbh->driver_data)->server));
  317. }
  318. /* }}} */
  319. /* {{{ mysql_handle_autocommit */
  320. static inline int mysql_handle_autocommit(pdo_dbh_t *dbh)
  321. {
  322. PDO_DBG_ENTER("mysql_handle_autocommit");
  323. PDO_DBG_INF_FMT("dbh=%p", dbh);
  324. PDO_DBG_INF_FMT("dbh->autocommit=%d", dbh->auto_commit);
  325. PDO_DBG_RETURN(0 <= mysql_autocommit(((pdo_mysql_db_handle *)dbh->driver_data)->server, dbh->auto_commit));
  326. }
  327. /* }}} */
  328. /* {{{ pdo_mysql_set_attribute */
  329. static int pdo_mysql_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
  330. {
  331. zend_long lval = zval_get_long(val);
  332. zend_bool bval = lval ? 1 : 0;
  333. PDO_DBG_ENTER("pdo_mysql_set_attribute");
  334. PDO_DBG_INF_FMT("dbh=%p", dbh);
  335. PDO_DBG_INF_FMT("attr=%l", attr);
  336. switch (attr) {
  337. case PDO_ATTR_AUTOCOMMIT:
  338. /* ignore if the new value equals the old one */
  339. if (dbh->auto_commit ^ bval) {
  340. dbh->auto_commit = bval;
  341. mysql_handle_autocommit(dbh);
  342. }
  343. PDO_DBG_RETURN(1);
  344. case PDO_ATTR_DEFAULT_STR_PARAM:
  345. ((pdo_mysql_db_handle *)dbh->driver_data)->assume_national_character_set_strings = lval == PDO_PARAM_STR_NATL ? 1 : 0;
  346. PDO_DBG_RETURN(1);
  347. case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY:
  348. /* ignore if the new value equals the old one */
  349. ((pdo_mysql_db_handle *)dbh->driver_data)->buffered = bval;
  350. PDO_DBG_RETURN(1);
  351. case PDO_MYSQL_ATTR_DIRECT_QUERY:
  352. case PDO_ATTR_EMULATE_PREPARES:
  353. /* ignore if the new value equals the old one */
  354. ((pdo_mysql_db_handle *)dbh->driver_data)->emulate_prepare = bval;
  355. PDO_DBG_RETURN(1);
  356. case PDO_ATTR_FETCH_TABLE_NAMES:
  357. ((pdo_mysql_db_handle *)dbh->driver_data)->fetch_table_names = bval;
  358. PDO_DBG_RETURN(1);
  359. #ifndef PDO_USE_MYSQLND
  360. case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE:
  361. if (lval < 0) {
  362. /* TODO: Johannes, can we throw a warning here? */
  363. ((pdo_mysql_db_handle *)dbh->driver_data)->max_buffer_size = 1024*1024;
  364. PDO_DBG_INF_FMT("Adjusting invalid buffer size to =%l", ((pdo_mysql_db_handle *)dbh->driver_data)->max_buffer_size);
  365. } else {
  366. ((pdo_mysql_db_handle *)dbh->driver_data)->max_buffer_size = lval;
  367. }
  368. PDO_DBG_RETURN(1);
  369. break;
  370. #endif
  371. default:
  372. PDO_DBG_RETURN(0);
  373. }
  374. }
  375. /* }}} */
  376. /* {{{ pdo_mysql_get_attribute */
  377. static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)
  378. {
  379. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  380. PDO_DBG_ENTER("pdo_mysql_get_attribute");
  381. PDO_DBG_INF_FMT("dbh=%p", dbh);
  382. PDO_DBG_INF_FMT("attr=%l", attr);
  383. switch (attr) {
  384. case PDO_ATTR_CLIENT_VERSION:
  385. ZVAL_STRING(return_value, (char *)mysql_get_client_info());
  386. break;
  387. case PDO_ATTR_SERVER_VERSION:
  388. ZVAL_STRING(return_value, (char *)mysql_get_server_info(H->server));
  389. break;
  390. case PDO_ATTR_CONNECTION_STATUS:
  391. ZVAL_STRING(return_value, (char *)mysql_get_host_info(H->server));
  392. break;
  393. case PDO_ATTR_SERVER_INFO: {
  394. #if defined(PDO_USE_MYSQLND)
  395. zend_string *tmp;
  396. if (mysqlnd_stat(H->server, &tmp) == PASS) {
  397. ZVAL_STR(return_value, tmp);
  398. #else
  399. char *tmp;
  400. if ((tmp = (char *)mysql_stat(H->server))) {
  401. ZVAL_STRING(return_value, tmp);
  402. #endif
  403. } else {
  404. pdo_mysql_error(dbh);
  405. PDO_DBG_RETURN(-1);
  406. }
  407. }
  408. break;
  409. case PDO_ATTR_AUTOCOMMIT:
  410. ZVAL_LONG(return_value, dbh->auto_commit);
  411. break;
  412. case PDO_ATTR_DEFAULT_STR_PARAM:
  413. ZVAL_LONG(return_value, H->assume_national_character_set_strings ? PDO_PARAM_STR_NATL : PDO_PARAM_STR_CHAR);
  414. break;
  415. case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY:
  416. ZVAL_LONG(return_value, H->buffered);
  417. break;
  418. case PDO_ATTR_EMULATE_PREPARES:
  419. case PDO_MYSQL_ATTR_DIRECT_QUERY:
  420. ZVAL_LONG(return_value, H->emulate_prepare);
  421. break;
  422. #ifndef PDO_USE_MYSQLND
  423. case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE:
  424. ZVAL_LONG(return_value, H->max_buffer_size);
  425. break;
  426. #else
  427. case PDO_MYSQL_ATTR_LOCAL_INFILE:
  428. ZVAL_BOOL(
  429. return_value,
  430. (H->server->data->options->flags & CLIENT_LOCAL_FILES) == CLIENT_LOCAL_FILES);
  431. break;
  432. #endif
  433. default:
  434. PDO_DBG_RETURN(0);
  435. }
  436. PDO_DBG_RETURN(1);
  437. }
  438. /* }}} */
  439. /* {{{ pdo_mysql_check_liveness */
  440. static int pdo_mysql_check_liveness(pdo_dbh_t *dbh)
  441. {
  442. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  443. PDO_DBG_ENTER("pdo_mysql_check_liveness");
  444. PDO_DBG_INF_FMT("dbh=%p", dbh);
  445. if (mysql_ping(H->server)) {
  446. PDO_DBG_RETURN(FAILURE);
  447. }
  448. PDO_DBG_RETURN(SUCCESS);
  449. }
  450. /* }}} */
  451. /* {{{ pdo_mysql_request_shutdown */
  452. static void pdo_mysql_request_shutdown(pdo_dbh_t *dbh)
  453. {
  454. pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
  455. PDO_DBG_ENTER("pdo_mysql_request_shutdown");
  456. PDO_DBG_INF_FMT("dbh=%p", dbh);
  457. #ifdef PDO_USE_MYSQLND
  458. if (H->server) {
  459. mysqlnd_end_psession(H->server);
  460. }
  461. #endif
  462. }
  463. /* }}} */
  464. /* {{{ mysql_methods */
  465. static const struct pdo_dbh_methods mysql_methods = {
  466. mysql_handle_closer,
  467. mysql_handle_preparer,
  468. mysql_handle_doer,
  469. mysql_handle_quoter,
  470. mysql_handle_begin,
  471. mysql_handle_commit,
  472. mysql_handle_rollback,
  473. pdo_mysql_set_attribute,
  474. pdo_mysql_last_insert_id,
  475. pdo_mysql_fetch_error_func,
  476. pdo_mysql_get_attribute,
  477. pdo_mysql_check_liveness,
  478. NULL,
  479. pdo_mysql_request_shutdown,
  480. NULL
  481. };
  482. /* }}} */
  483. #ifdef PHP_WIN32
  484. # define PDO_DEFAULT_MYSQL_UNIX_ADDR NULL
  485. #else
  486. # define PDO_DEFAULT_MYSQL_UNIX_ADDR PDO_MYSQL_G(default_socket)
  487. #endif
  488. /* {{{ pdo_mysql_handle_factory */
  489. static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
  490. {
  491. pdo_mysql_db_handle *H;
  492. size_t i;
  493. int ret = 0;
  494. char *host = NULL, *unix_socket = NULL;
  495. unsigned int port = 3306;
  496. char *dbname;
  497. struct pdo_data_src_parser vars[] = {
  498. { "charset", NULL, 0 },
  499. { "dbname", "", 0 },
  500. { "host", "localhost", 0 },
  501. { "port", "3306", 0 },
  502. { "unix_socket", PDO_DEFAULT_MYSQL_UNIX_ADDR, 0 },
  503. };
  504. int connect_opts = 0
  505. #ifdef CLIENT_MULTI_RESULTS
  506. |CLIENT_MULTI_RESULTS
  507. #endif
  508. ;
  509. #if defined(PDO_USE_MYSQLND)
  510. size_t dbname_len = 0;
  511. size_t password_len = 0;
  512. #endif
  513. #ifdef CLIENT_MULTI_STATEMENTS
  514. if (!driver_options) {
  515. connect_opts |= CLIENT_MULTI_STATEMENTS;
  516. } else if (pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_MULTI_STATEMENTS, 1)) {
  517. connect_opts |= CLIENT_MULTI_STATEMENTS;
  518. }
  519. #endif
  520. PDO_DBG_ENTER("pdo_mysql_handle_factory");
  521. PDO_DBG_INF_FMT("dbh=%p", dbh);
  522. #ifdef CLIENT_MULTI_RESULTS
  523. PDO_DBG_INF("multi results");
  524. #endif
  525. php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 5);
  526. H = pecalloc(1, sizeof(pdo_mysql_db_handle), dbh->is_persistent);
  527. H->einfo.errcode = 0;
  528. H->einfo.errmsg = NULL;
  529. /* allocate an environment */
  530. /* handle for the server */
  531. if (!(H->server = pdo_mysql_init(dbh->is_persistent))) {
  532. pdo_mysql_error(dbh);
  533. goto cleanup;
  534. }
  535. #if defined(PDO_USE_MYSQLND)
  536. if (dbh->is_persistent) {
  537. mysqlnd_restart_psession(H->server);
  538. }
  539. #endif
  540. dbh->driver_data = H;
  541. dbh->skip_param_evt =
  542. 1 << PDO_PARAM_EVT_FREE |
  543. 1 << PDO_PARAM_EVT_EXEC_POST |
  544. 1 << PDO_PARAM_EVT_FETCH_PRE |
  545. 1 << PDO_PARAM_EVT_FETCH_POST |
  546. 1 << PDO_PARAM_EVT_NORMALIZE;
  547. #ifndef PDO_USE_MYSQLND
  548. H->max_buffer_size = 1024*1024;
  549. #endif
  550. H->assume_national_character_set_strings = 0;
  551. H->buffered = H->emulate_prepare = 1;
  552. /* handle MySQL options */
  553. if (driver_options) {
  554. zend_long connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);
  555. unsigned int local_infile = (unsigned int) pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_LOCAL_INFILE, 0);
  556. zend_string *init_cmd = NULL;
  557. #ifndef PDO_USE_MYSQLND
  558. zend_string *default_file = NULL, *default_group = NULL;
  559. #endif
  560. zend_long compress = 0;
  561. zend_string *ssl_key = NULL, *ssl_cert = NULL, *ssl_ca = NULL, *ssl_capath = NULL, *ssl_cipher = NULL;
  562. H->buffered = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_USE_BUFFERED_QUERY, 1);
  563. H->emulate_prepare = pdo_attr_lval(driver_options,
  564. PDO_MYSQL_ATTR_DIRECT_QUERY, H->emulate_prepare);
  565. H->emulate_prepare = pdo_attr_lval(driver_options,
  566. PDO_ATTR_EMULATE_PREPARES, H->emulate_prepare);
  567. H->assume_national_character_set_strings = pdo_attr_lval(driver_options,
  568. PDO_ATTR_DEFAULT_STR_PARAM, 0) == PDO_PARAM_STR_NATL ? 1 : 0;
  569. #ifndef PDO_USE_MYSQLND
  570. H->max_buffer_size = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_MAX_BUFFER_SIZE, H->max_buffer_size);
  571. #endif
  572. if (pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_FOUND_ROWS, 0)) {
  573. connect_opts |= CLIENT_FOUND_ROWS;
  574. }
  575. if (pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_IGNORE_SPACE, 0)) {
  576. connect_opts |= CLIENT_IGNORE_SPACE;
  577. }
  578. if (mysql_options(H->server, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout)) {
  579. pdo_mysql_error(dbh);
  580. goto cleanup;
  581. }
  582. #ifndef PDO_USE_MYSQLND
  583. if (PG(open_basedir) && PG(open_basedir)[0] != '\0') {
  584. local_infile = 0;
  585. }
  586. #endif
  587. #if defined(MYSQL_OPT_LOCAL_INFILE) || defined(PDO_USE_MYSQLND)
  588. if (mysql_options(H->server, MYSQL_OPT_LOCAL_INFILE, (const char *)&local_infile)) {
  589. pdo_mysql_error(dbh);
  590. goto cleanup;
  591. }
  592. #endif
  593. #ifdef MYSQL_OPT_RECONNECT
  594. /* since 5.0.3, the default for this option is 0 if not specified.
  595. * we want the old behaviour
  596. * mysqlnd doesn't support reconnect, thus we don't have "|| defined(PDO_USE_MYSQLND)"
  597. */
  598. {
  599. zend_long reconnect = 1;
  600. mysql_options(H->server, MYSQL_OPT_RECONNECT, (const char*)&reconnect);
  601. }
  602. #endif
  603. init_cmd = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_INIT_COMMAND, NULL);
  604. if (init_cmd) {
  605. if (mysql_options(H->server, MYSQL_INIT_COMMAND, (const char *)ZSTR_VAL(init_cmd))) {
  606. zend_string_release_ex(init_cmd, 0);
  607. pdo_mysql_error(dbh);
  608. goto cleanup;
  609. }
  610. zend_string_release_ex(init_cmd, 0);
  611. }
  612. #ifndef PDO_USE_MYSQLND
  613. default_file = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_READ_DEFAULT_FILE, NULL);
  614. if (default_file) {
  615. if (mysql_options(H->server, MYSQL_READ_DEFAULT_FILE, (const char *)ZSTR_VAL(default_file))) {
  616. zend_string_release_ex(default_file, 0);
  617. pdo_mysql_error(dbh);
  618. goto cleanup;
  619. }
  620. zend_string_release_ex(default_file, 0);
  621. }
  622. default_group = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_READ_DEFAULT_GROUP, NULL);
  623. if (default_group) {
  624. if (mysql_options(H->server, MYSQL_READ_DEFAULT_GROUP, (const char *)ZSTR_VAL(default_group))) {
  625. zend_string_release_ex(default_group, 0);
  626. pdo_mysql_error(dbh);
  627. goto cleanup;
  628. }
  629. zend_string_release_ex(default_group, 0);
  630. }
  631. #endif
  632. compress = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_COMPRESS, 0);
  633. if (compress) {
  634. if (mysql_options(H->server, MYSQL_OPT_COMPRESS, 0)) {
  635. pdo_mysql_error(dbh);
  636. goto cleanup;
  637. }
  638. }
  639. ssl_key = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_KEY, NULL);
  640. ssl_cert = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CERT, NULL);
  641. ssl_ca = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CA, NULL);
  642. ssl_capath = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CAPATH, NULL);
  643. ssl_cipher = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CIPHER, NULL);
  644. if (ssl_key || ssl_cert || ssl_ca || ssl_capath || ssl_cipher) {
  645. mysql_ssl_set(H->server,
  646. ssl_key? ZSTR_VAL(ssl_key) : NULL,
  647. ssl_cert? ZSTR_VAL(ssl_cert) : NULL,
  648. ssl_ca? ZSTR_VAL(ssl_ca) : NULL,
  649. ssl_capath? ZSTR_VAL(ssl_capath) : NULL,
  650. ssl_cipher? ZSTR_VAL(ssl_cipher) : NULL);
  651. if (ssl_key) {
  652. zend_string_release_ex(ssl_key, 0);
  653. }
  654. if (ssl_cert) {
  655. zend_string_release_ex(ssl_cert, 0);
  656. }
  657. if (ssl_ca) {
  658. zend_string_release_ex(ssl_ca, 0);
  659. }
  660. if (ssl_capath) {
  661. zend_string_release_ex(ssl_capath, 0);
  662. }
  663. if (ssl_cipher) {
  664. zend_string_release_ex(ssl_cipher, 0);
  665. }
  666. }
  667. #if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND)
  668. {
  669. zend_string *public_key = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY, NULL);
  670. if (public_key) {
  671. if (mysql_options(H->server, MYSQL_SERVER_PUBLIC_KEY, ZSTR_VAL(public_key))) {
  672. pdo_mysql_error(dbh);
  673. zend_string_release_ex(public_key, 0);
  674. goto cleanup;
  675. }
  676. zend_string_release_ex(public_key, 0);
  677. }
  678. }
  679. #endif
  680. #ifdef PDO_USE_MYSQLND
  681. {
  682. zend_long ssl_verify_cert = pdo_attr_lval(driver_options,
  683. PDO_MYSQL_ATTR_SSL_VERIFY_SERVER_CERT, -1);
  684. if (ssl_verify_cert != -1) {
  685. connect_opts |= ssl_verify_cert ?
  686. CLIENT_SSL_VERIFY_SERVER_CERT:
  687. CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
  688. }
  689. }
  690. #endif
  691. } else {
  692. #if defined(MYSQL_OPT_LOCAL_INFILE) || defined(PDO_USE_MYSQLND)
  693. // in case there are no driver options disable 'local infile' explicitly
  694. unsigned int local_infile = 0;
  695. if (mysql_options(H->server, MYSQL_OPT_LOCAL_INFILE, (const char *)&local_infile)) {
  696. pdo_mysql_error(dbh);
  697. goto cleanup;
  698. }
  699. #endif
  700. }
  701. #ifdef PDO_MYSQL_HAS_CHARSET
  702. if (vars[0].optval && mysql_options(H->server, MYSQL_SET_CHARSET_NAME, vars[0].optval)) {
  703. pdo_mysql_error(dbh);
  704. goto cleanup;
  705. }
  706. #endif
  707. dbname = vars[1].optval;
  708. host = vars[2].optval;
  709. if(vars[3].optval) {
  710. port = atoi(vars[3].optval);
  711. }
  712. #ifdef PHP_WIN32
  713. if (vars[2].optval && !strcmp(".", vars[2].optval)) {
  714. #else
  715. if (vars[2].optval && !strcmp("localhost", vars[2].optval)) {
  716. #endif
  717. unix_socket = vars[4].optval;
  718. }
  719. /* TODO: - Check zval cache + ZTS */
  720. #ifdef PDO_USE_MYSQLND
  721. if (dbname) {
  722. dbname_len = strlen(dbname);
  723. }
  724. if (dbh->password) {
  725. password_len = strlen(dbh->password);
  726. }
  727. if (mysqlnd_connect(H->server, host, dbh->username, dbh->password, password_len, dbname, dbname_len,
  728. port, unix_socket, connect_opts, MYSQLND_CLIENT_NO_FLAG) == NULL) {
  729. #else
  730. if (mysql_real_connect(H->server, host, dbh->username, dbh->password, dbname, port, unix_socket, connect_opts) == NULL) {
  731. #endif
  732. pdo_mysql_error(dbh);
  733. goto cleanup;
  734. }
  735. if (!dbh->auto_commit) {
  736. mysql_handle_autocommit(dbh);
  737. }
  738. H->attached = 1;
  739. dbh->alloc_own_columns = 1;
  740. dbh->max_escaped_char_length = 2;
  741. dbh->methods = &mysql_methods;
  742. ret = 1;
  743. cleanup:
  744. for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) {
  745. if (vars[i].freeme) {
  746. efree(vars[i].optval);
  747. }
  748. }
  749. dbh->methods = &mysql_methods;
  750. PDO_DBG_RETURN(ret);
  751. }
  752. /* }}} */
  753. const pdo_driver_t pdo_mysql_driver = {
  754. PDO_DRIVER_HEADER(mysql),
  755. pdo_mysql_handle_factory
  756. };
  757. /*
  758. * Local variables:
  759. * tab-width: 4
  760. * c-basic-offset: 4
  761. * End:
  762. * vim600: noet sw=4 ts=4 fdm=marker
  763. * vim<600: noet sw=4 ts=4
  764. */