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