mysql_driver.c 27 KB

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