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