2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/pdo_mysql.h"
19 #include "hphp/runtime/ext/ext_stream.h"
22 #ifdef PHP_MYSQL_UNIX_SOCK_ADDR
23 #ifdef MYSQL_UNIX_ADDR
24 #undef MYSQL_UNIX_ADDR
26 #define MYSQL_UNIX_ADDR PHP_MYSQL_UNIX_SOCK_ADDR
30 IMPLEMENT_DEFAULT_EXTENSION(pdo_mysql
);
32 ///////////////////////////////////////////////////////////////////////////////
36 PDOMySqlError() : file(NULL
), line(0), errcode(0), errmsg(NULL
) {
45 class PDOMySqlStatement
;
46 class PDOMySqlConnection
: public PDOConnection
{
49 virtual ~PDOMySqlConnection();
50 virtual bool create(CArrRef options
);
52 int handleError(const char *file
, int line
, PDOMySqlStatement
*stmt
= NULL
);
54 virtual bool support(SupportedMethod method
);
55 virtual bool closer();
56 virtual bool preparer(CStrRef sql
, sp_PDOStatement
*stmt
, CVarRef options
);
57 virtual int64_t doer(CStrRef sql
);
58 virtual bool quoter(CStrRef input
, String
"ed
, PDOParamType paramtype
);
60 virtual bool commit();
61 virtual bool rollback();
62 virtual bool setAttribute(int64_t attr
, CVarRef value
);
63 virtual String
lastId(const char *name
);
64 virtual bool fetchErr(PDOStatement
*stmt
, Array
&info
);
65 virtual int getAttribute(int64_t attr
, Variant
&value
);
66 virtual bool checkLiveness();
67 virtual void persistentShutdown();
69 bool buffered() const { return m_buffered
;}
70 unsigned long max_buffer_size() const { return m_max_buffer_size
;}
71 bool fetch_table_names() const { return m_fetch_table_names
;}
75 unsigned m_attached
:1;
76 unsigned m_buffered
:1;
77 unsigned m_emulate_prepare
:1;
78 unsigned m_fetch_table_names
:1;
79 unsigned long m_max_buffer_size
;
80 PDOMySqlError m_einfo
;
83 class PDOMySqlStatement
: public PDOStatement
{
85 PDOMySqlStatement(PDOMySqlConnection
*conn
, MYSQL
*server
);
86 virtual ~PDOMySqlStatement();
88 bool create(CStrRef sql
, CArrRef options
);
90 virtual bool support(SupportedMethod method
);
91 virtual bool executer();
92 virtual bool fetcher(PDOFetchOrientation ori
, long offset
);
93 virtual bool describer(int colno
);
94 virtual bool getColumn(int colno
, Variant
&value
);
95 virtual bool paramHook(PDOBoundParam
*param
, PDOParamEvent event_type
);
96 virtual bool getColumnMeta(int64_t colno
, Array
&return_value
);
97 virtual bool nextRowset();
98 virtual bool cursorCloser();
100 MYSQL_STMT
*stmt() { return m_stmt
;}
103 PDOMySqlConnection
*m_conn
;
106 const MYSQL_FIELD
*m_fields
;
107 MYSQL_ROW m_current_data
;
108 long *m_current_lengths
;
109 PDOMySqlError m_einfo
;
112 MYSQL_BIND
*m_params
;
114 unsigned long *m_in_length
;
115 MYSQL_BIND
*m_bound_result
;
117 unsigned long *m_out_length
;
118 unsigned int m_params_given
;
119 unsigned m_max_length
:1;
122 bool executePrepared();
123 int handleError(const char *file
, int line
);
126 ///////////////////////////////////////////////////////////////////////////////
128 /* For the convenience of drivers, this function will parse a data source
129 * string, of the form "name=value; name2=value2" and populate variables
130 * according to the data you pass in and array of pdo_data_src_parser structures */
131 struct pdo_data_src_parser
{
137 static int php_pdo_parse_data_source(const char *data_source
,
139 struct pdo_data_src_parser
*parsed
,
149 while (i
< data_source_len
) {
150 /* looking for NAME= */
152 if (data_source
[i
] == '\0') {
156 if (data_source
[i
] != '=') {
163 /* now we're looking for VALUE; or just VALUE<NUL> */
165 while (i
< data_source_len
) {
166 if (data_source
[i
] == '\0') {
170 if (data_source
[i
] == ';') {
181 /* find the entry in the array */
182 nlen
= valstart
- optstart
- 1;
183 for (j
= 0; j
< nparams
; j
++) {
184 if (0 == strncmp(data_source
+ optstart
, parsed
[j
].optname
, nlen
) &&
185 parsed
[j
].optname
[nlen
] == '\0') {
187 if (parsed
[j
].freeme
) {
188 free(parsed
[j
].optval
);
190 parsed
[j
].optval
= strndup(data_source
+ valstart
, semi
- valstart
);
191 parsed
[j
].freeme
= 1;
197 while (i
< data_source_len
&& isspace(data_source
[i
])) {
207 static long pdo_attr_lval(CArrRef options
, int opt
, long defaultValue
) {
208 if (options
.exists(opt
)) {
209 return options
[opt
].toInt64();
214 static String
pdo_attr_strval(CArrRef options
, int opt
, const char *def
) {
215 if (options
.exists(opt
)) {
216 return options
[opt
].toString();
224 ///////////////////////////////////////////////////////////////////////////////
226 PDOMySqlConnection::PDOMySqlConnection()
227 : m_server(NULL
), m_attached(0), m_buffered(0), m_emulate_prepare(0),
228 m_fetch_table_names(0), m_max_buffer_size(0) {
231 PDOMySqlConnection::~PDOMySqlConnection() {
233 mysql_close(m_server
);
235 if (m_einfo
.errmsg
) {
236 free(m_einfo
.errmsg
);
240 bool PDOMySqlConnection::create(CArrRef options
) {
242 char *host
= NULL
, *unix_socket
= NULL
;
243 unsigned int port
= 3306;
245 struct pdo_data_src_parser vars
[] = {
246 { "charset", NULL
, 0 },
248 { "host", "localhost", 0 },
249 { "port", "3306", 0 },
250 { "unix_socket", MYSQL_UNIX_ADDR
, 0 },
253 #ifdef CLIENT_MULTI_RESULTS
254 |CLIENT_MULTI_RESULTS
256 #ifdef CLIENT_MULTI_STATEMENTS
257 |CLIENT_MULTI_STATEMENTS
261 php_pdo_parse_data_source(data_source
.data(), data_source
.size(), vars
, 5);
263 /* handle for the server */
264 if (!(m_server
= mysql_init(NULL
))) {
265 handleError(__FILE__
, __LINE__
);
269 m_max_buffer_size
= 1024*1024;
270 m_buffered
= m_emulate_prepare
= 1;
272 /* handle MySQL options */
273 if (!options
.empty()) {
274 long connect_timeout
= pdo_attr_lval(options
, PDO_ATTR_TIMEOUT
, 30);
275 long local_infile
= pdo_attr_lval(options
, PDO_MYSQL_ATTR_LOCAL_INFILE
, 0);
276 String init_cmd
, default_file
, default_group
;
278 m_buffered
= pdo_attr_lval(options
, PDO_MYSQL_ATTR_USE_BUFFERED_QUERY
, 1);
280 m_emulate_prepare
= pdo_attr_lval(options
, PDO_MYSQL_ATTR_DIRECT_QUERY
,
282 m_emulate_prepare
= pdo_attr_lval(options
, PDO_ATTR_EMULATE_PREPARES
,
285 m_max_buffer_size
= pdo_attr_lval(options
, PDO_MYSQL_ATTR_MAX_BUFFER_SIZE
,
288 if (pdo_attr_lval(options
, PDO_MYSQL_ATTR_FOUND_ROWS
, 0)) {
289 connect_opts
|= CLIENT_FOUND_ROWS
;
291 if (pdo_attr_lval(options
, PDO_MYSQL_ATTR_IGNORE_SPACE
, 0)) {
292 connect_opts
|= CLIENT_IGNORE_SPACE
;
295 if (mysql_options(m_server
, MYSQL_OPT_CONNECT_TIMEOUT
,
296 (const char *)&connect_timeout
)) {
297 handleError(__FILE__
, __LINE__
);
301 if (mysql_options(m_server
, MYSQL_OPT_LOCAL_INFILE
,
302 (const char *)&local_infile
)) {
303 handleError(__FILE__
, __LINE__
);
306 #ifdef MYSQL_OPT_RECONNECT
307 /* since 5.0.3, the default for this option is 0 if not specified.
308 * we want the old behaviour */
311 mysql_options(m_server
, MYSQL_OPT_RECONNECT
, (const char*)&reconnect
);
314 init_cmd
= pdo_attr_strval(options
, PDO_MYSQL_ATTR_INIT_COMMAND
, NULL
);
315 if (!init_cmd
.empty()) {
316 if (mysql_options(m_server
, MYSQL_INIT_COMMAND
, init_cmd
.data())) {
317 handleError(__FILE__
, __LINE__
);
322 default_file
= pdo_attr_strval(options
, PDO_MYSQL_ATTR_READ_DEFAULT_FILE
,
324 if (!default_file
.empty()) {
325 if (mysql_options(m_server
, MYSQL_READ_DEFAULT_FILE
,
326 default_file
.data())) {
327 handleError(__FILE__
, __LINE__
);
332 default_group
= pdo_attr_strval(options
, PDO_MYSQL_ATTR_READ_DEFAULT_GROUP
,
334 if (!default_group
.empty()) {
335 if (mysql_options(m_server
, MYSQL_READ_DEFAULT_GROUP
,
336 default_group
.data())) {
337 handleError(__FILE__
, __LINE__
);
342 compress
= pdo_attr_lval(options
, PDO_MYSQL_ATTR_COMPRESS
, 0);
344 if (mysql_options(m_server
, MYSQL_OPT_COMPRESS
, 0)) {
345 handleError(__FILE__
, __LINE__
);
351 dbname
= vars
[1].optval
;
352 host
= vars
[2].optval
;
353 if (vars
[3].optval
) {
354 port
= atoi(vars
[3].optval
);
356 if (vars
[2].optval
&& !strcmp("localhost", vars
[2].optval
)) {
357 unix_socket
= vars
[4].optval
;
360 /* TODO: - Check zval cache + ZTS */
361 if (mysql_real_connect(m_server
, host
, username
.c_str(), password
.c_str(),
362 dbname
, port
, unix_socket
, connect_opts
) == NULL
) {
363 handleError(__FILE__
, __LINE__
);
368 mysql_autocommit(m_server
, auto_commit
);
373 alloc_own_columns
= 1;
374 max_escaped_char_length
= 2;
379 for (i
= 0; i
< (int)(sizeof(vars
)/sizeof(vars
[0])); i
++) {
380 if (vars
[i
].freeme
) {
381 free(vars
[i
].optval
);
388 bool PDOMySqlConnection::support(SupportedMethod method
) {
392 bool PDOMySqlConnection::closer() {
394 mysql_close(m_server
);
397 if (m_einfo
.errmsg
) {
398 free(m_einfo
.errmsg
);
399 m_einfo
.errmsg
= NULL
;
404 int PDOMySqlConnection::handleError(const char *file
, int line
,
405 PDOMySqlStatement
*stmt
) {
406 PDOErrorType
*pdo_err
;
407 PDOMySqlError
*einfo
= &m_einfo
;
410 pdo_err
= &stmt
->error_code
;
412 pdo_err
= &error_code
;
415 if (stmt
&& stmt
->stmt()) {
416 einfo
->errcode
= mysql_stmt_errno(stmt
->stmt());
418 einfo
->errcode
= mysql_errno(m_server
);
426 einfo
->errmsg
= NULL
;
429 if (einfo
->errcode
) {
430 if (einfo
->errcode
== 2014) {
432 strdup("Cannot execute queries while other unbuffered queries are "
433 "active. Consider using PDOStatement::fetchAll(). "
434 "Alternatively, if your code is only ever going to run against "
435 "mysql, you may enable query buffering by setting the "
436 "PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.");
437 } else if (einfo
->errcode
== 2057) {
439 strdup("A stored procedure returning result sets of different size "
440 "was called. This is not supported by libmysql");
442 einfo
->errmsg
= strdup(mysql_error(m_server
));
444 } else { /* no error */
445 strcpy(*pdo_err
, PDO_ERR_NONE
);
449 if (stmt
&& stmt
->stmt()) {
450 strcpy(*pdo_err
, mysql_stmt_sqlstate(stmt
->stmt()));
452 strcpy(*pdo_err
, mysql_sqlstate(m_server
));
455 if (stmt
&& stmt
->stmt()) {
456 pdo_raise_impl_error(stmt
->dbh
, NULL
, pdo_err
[0], einfo
->errmsg
);
458 throw_pdo_exception((int)einfo
->errcode
, uninit_null(), "SQLSTATE[%s] [%d] %s",
459 pdo_err
[0], einfo
->errcode
, einfo
->errmsg
);
461 return einfo
->errcode
;
464 bool PDOMySqlConnection::preparer(CStrRef sql
, sp_PDOStatement
*stmt
,
466 PDOMySqlStatement
*s
= new PDOMySqlStatement(this, m_server
);
469 if (m_emulate_prepare
) {
472 int server_version
= mysql_get_server_version(m_server
);
473 if (server_version
< 40100) {
477 if (s
->create(sql
, options
.toArray())) {
478 alloc_own_columns
= 1;
483 strcpy(error_code
, s
->error_code
);
487 int64_t PDOMySqlConnection::doer(CStrRef sql
) {
488 if (mysql_real_query(m_server
, sql
.data(), sql
.size())) {
489 handleError(__FILE__
, __LINE__
);
493 my_ulonglong c
= mysql_affected_rows(m_server
);
494 if (c
== (my_ulonglong
) -1) {
495 handleError(__FILE__
, __LINE__
);
496 return m_einfo
.errcode
? -1 : 0;
499 /* MULTI_QUERY support - eat up all unfetched result sets */
500 while (mysql_more_results(m_server
)) {
501 if (mysql_next_result(m_server
)) {
504 MYSQL_RES
*result
= mysql_store_result(m_server
);
506 mysql_free_result(result
);
512 bool PDOMySqlConnection::quoter(CStrRef input
, String
"ed
,
513 PDOParamType paramtype
) {
514 String
s(2 * input
.size() + 3, ReserveString
);
515 char *buf
= s
.mutableSlice().ptr
;
516 int len
= mysql_real_escape_string(m_server
, buf
+ 1,
517 input
.data(), input
.size());
519 buf
[0] = buf
[len
] = '\'';
521 quoted
= s
.setSize(len
);
525 bool PDOMySqlConnection::begin() {
526 return doer("START TRANSACTION") >= 0;
529 bool PDOMySqlConnection::commit() {
530 return mysql_commit(m_server
) >= 0;
533 bool PDOMySqlConnection::rollback() {
534 return mysql_rollback(m_server
) >= 0;
537 bool PDOMySqlConnection::setAttribute(int64_t attr
, CVarRef value
) {
539 case PDO_ATTR_AUTOCOMMIT
:
540 /* ignore if the new value equals the old one */
541 if (auto_commit
^ value
.toBoolean()) {
542 auto_commit
= value
.toBoolean();
543 mysql_autocommit(m_server
, auto_commit
);
547 case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY
:
548 m_buffered
= value
.toBoolean();
550 case PDO_MYSQL_ATTR_DIRECT_QUERY
:
551 case PDO_ATTR_EMULATE_PREPARES
:
552 m_emulate_prepare
= value
.toBoolean();
554 case PDO_ATTR_FETCH_TABLE_NAMES
:
555 m_fetch_table_names
= value
.toBoolean();
557 case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE
:
558 if (value
.toInt64() < 0) {
559 /* TODO: Johannes, can we throw a warning here? */
560 m_max_buffer_size
= 1024*1024;
562 m_max_buffer_size
= value
.toInt64();
570 String
PDOMySqlConnection::lastId(const char *name
) {
571 return (int64_t)mysql_insert_id(m_server
);
574 bool PDOMySqlConnection::fetchErr(PDOStatement
*stmt
, Array
&info
) {
575 if (m_einfo
.errcode
) {
576 info
.append((int64_t)m_einfo
.errcode
);
577 info
.append(String(m_einfo
.errmsg
, CopyString
));
582 int PDOMySqlConnection::getAttribute(int64_t attr
, Variant
&value
) {
584 case PDO_ATTR_CLIENT_VERSION
:
585 value
= String((char *)mysql_get_client_info(), CopyString
);
587 case PDO_ATTR_SERVER_VERSION
:
588 value
= String((char *)mysql_get_server_info(m_server
), CopyString
);
590 case PDO_ATTR_CONNECTION_STATUS
:
591 value
= String((char *)mysql_get_host_info(m_server
), CopyString
);
593 case PDO_ATTR_SERVER_INFO
: {
594 char *tmp
= (char *)mysql_stat(m_server
);
596 value
= String(tmp
, CopyString
);
598 handleError(__FILE__
, __LINE__
);
603 case PDO_ATTR_AUTOCOMMIT
:
604 value
= (int64_t)auto_commit
;
606 case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY
:
607 value
= (int64_t)m_buffered
;
609 case PDO_MYSQL_ATTR_DIRECT_QUERY
:
610 value
= (int64_t)m_emulate_prepare
;
612 case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE
:
613 value
= (int64_t)m_max_buffer_size
;
621 bool PDOMySqlConnection::checkLiveness() {
622 return !mysql_ping(m_server
);
625 void PDOMySqlConnection::persistentShutdown() {
629 ///////////////////////////////////////////////////////////////////////////////
631 void PDOMySqlStatement::setRowCount() {
632 my_ulonglong count
= mysql_stmt_affected_rows(m_stmt
);
633 if (count
!= (my_ulonglong
)-1) {
638 bool PDOMySqlStatement::executePrepared() {
639 /* (re)bind the parameters */
640 if (mysql_stmt_bind_param(m_stmt
, m_params
) || mysql_stmt_execute(m_stmt
)) {
645 handleError(__FILE__
, __LINE__
);
646 if (mysql_stmt_errno(m_stmt
) == 2057) {
647 /* CR_NEW_STMT_METADATA makes the statement unusable */
656 /* figure out the result set format, if any */
657 m_result
= mysql_stmt_result_metadata(m_stmt
);
659 int calc_max_length
= m_conn
->buffered() && m_max_length
== 1;
660 m_fields
= mysql_fetch_fields(m_result
);
661 if (m_bound_result
) {
663 for (i
= 0; i
< column_count
; i
++) {
664 free(m_bound_result
[i
].buffer
);
666 free(m_bound_result
);
671 column_count
= (int)mysql_num_fields(m_result
);
672 m_bound_result
= (MYSQL_BIND
*)calloc(column_count
, sizeof(MYSQL_BIND
));
673 m_out_null
= (my_bool
*)calloc(column_count
, sizeof(my_bool
));
674 m_out_length
= (unsigned long *)calloc(column_count
,
675 sizeof(unsigned long));
677 /* summon memory to hold the row */
678 for (i
= 0; i
< column_count
; i
++) {
679 if (calc_max_length
&& m_fields
[i
].type
== FIELD_TYPE_BLOB
) {
681 mysql_stmt_attr_set(m_stmt
, STMT_ATTR_UPDATE_MAX_LENGTH
, &on
);
684 switch (m_fields
[i
].type
) {
685 case FIELD_TYPE_INT24
:
686 m_bound_result
[i
].buffer_length
= MAX_MEDIUMINT_WIDTH
+ 1;
688 case FIELD_TYPE_LONG
:
689 m_bound_result
[i
].buffer_length
= MAX_INT_WIDTH
+ 1;
691 case FIELD_TYPE_LONGLONG
:
692 m_bound_result
[i
].buffer_length
= MAX_BIGINT_WIDTH
+ 1;
694 case FIELD_TYPE_TINY
:
695 m_bound_result
[i
].buffer_length
= MAX_TINYINT_WIDTH
+ 1;
697 case FIELD_TYPE_SHORT
:
698 m_bound_result
[i
].buffer_length
= MAX_SMALLINT_WIDTH
+ 1;
701 m_bound_result
[i
].buffer_length
=
702 m_fields
[i
].max_length
? m_fields
[i
].max_length
:
704 /* work-around for longtext and alike */
705 if (m_bound_result
[i
].buffer_length
> m_conn
->max_buffer_size()) {
706 m_bound_result
[i
].buffer_length
= m_conn
->max_buffer_size();
710 /* there are cases where the length reported by mysql is too short.
711 * eg: when describing a table that contains an enum column. Since
712 * we have no way of knowing the true length either, we'll bump up
713 * our buffer size to a reasonable size, just in case */
714 if (m_fields
[i
].max_length
== 0 &&
715 m_bound_result
[i
].buffer_length
< 128 && MYSQL_TYPE_VAR_STRING
) {
716 m_bound_result
[i
].buffer_length
= 128;
721 m_bound_result
[i
].buffer
= malloc(m_bound_result
[i
].buffer_length
);
722 m_bound_result
[i
].is_null
= &m_out_null
[i
];
723 m_bound_result
[i
].length
= &m_out_length
[i
];
724 m_bound_result
[i
].buffer_type
= MYSQL_TYPE_STRING
;
727 if (mysql_stmt_bind_result(m_stmt
, m_bound_result
)) {
728 handleError(__FILE__
, __LINE__
);
732 /* if buffered, pre-fetch all the data */
733 if (m_conn
->buffered()) {
734 mysql_stmt_store_result(m_stmt
);
743 static const char *type_to_name_native(int type
) {
744 #define PDO_MYSQL_NATIVE_TYPE_NAME(x) case FIELD_TYPE_##x: return #x;
747 PDO_MYSQL_NATIVE_TYPE_NAME(STRING
)
748 PDO_MYSQL_NATIVE_TYPE_NAME(VAR_STRING
)
749 #ifdef MYSQL_HAS_TINY
750 PDO_MYSQL_NATIVE_TYPE_NAME(TINY
)
752 PDO_MYSQL_NATIVE_TYPE_NAME(SHORT
)
753 PDO_MYSQL_NATIVE_TYPE_NAME(LONG
)
754 PDO_MYSQL_NATIVE_TYPE_NAME(LONGLONG
)
755 PDO_MYSQL_NATIVE_TYPE_NAME(INT24
)
756 PDO_MYSQL_NATIVE_TYPE_NAME(FLOAT
)
757 PDO_MYSQL_NATIVE_TYPE_NAME(DOUBLE
)
758 PDO_MYSQL_NATIVE_TYPE_NAME(DECIMAL
)
759 #ifdef FIELD_TYPE_NEWDECIMAL
760 PDO_MYSQL_NATIVE_TYPE_NAME(NEWDECIMAL
)
762 #ifdef FIELD_TYPE_GEOMETRY
763 PDO_MYSQL_NATIVE_TYPE_NAME(GEOMETRY
)
765 PDO_MYSQL_NATIVE_TYPE_NAME(TIMESTAMP
)
766 #ifdef MYSQL_HAS_YEAR
767 PDO_MYSQL_NATIVE_TYPE_NAME(YEAR
)
769 PDO_MYSQL_NATIVE_TYPE_NAME(SET
)
770 PDO_MYSQL_NATIVE_TYPE_NAME(ENUM
)
771 PDO_MYSQL_NATIVE_TYPE_NAME(DATE
)
772 #ifdef FIELD_TYPE_NEWDATE
773 PDO_MYSQL_NATIVE_TYPE_NAME(NEWDATE
)
775 PDO_MYSQL_NATIVE_TYPE_NAME(TIME
)
776 PDO_MYSQL_NATIVE_TYPE_NAME(DATETIME
)
777 PDO_MYSQL_NATIVE_TYPE_NAME(TINY_BLOB
)
778 PDO_MYSQL_NATIVE_TYPE_NAME(MEDIUM_BLOB
)
779 PDO_MYSQL_NATIVE_TYPE_NAME(LONG_BLOB
)
780 PDO_MYSQL_NATIVE_TYPE_NAME(BLOB
)
781 PDO_MYSQL_NATIVE_TYPE_NAME(NULL
)
785 #undef PDO_MYSQL_NATIVE_TYPE_NAME
788 ///////////////////////////////////////////////////////////////////////////////
790 PDOMySqlStatement::PDOMySqlStatement(PDOMySqlConnection
*conn
, MYSQL
*server
)
791 : m_conn(conn
), m_server(server
), m_result(NULL
), m_fields(NULL
),
792 m_current_data(NULL
), m_current_lengths(NULL
), m_stmt(NULL
),
793 m_num_params(0), m_params(NULL
), m_in_null(NULL
), m_in_length(NULL
),
794 m_bound_result(NULL
), m_out_null(NULL
), m_out_length(NULL
),
795 m_params_given(0), m_max_length(0) {
798 PDOMySqlStatement::~PDOMySqlStatement() {
800 /* free the resource */
801 mysql_free_result(m_result
);
804 if (m_einfo
.errmsg
) {
805 free(m_einfo
.errmsg
);
806 m_einfo
.errmsg
= NULL
;
809 mysql_stmt_close(m_stmt
);
823 if (m_bound_result
) {
825 for (i
= 0; i
< column_count
; i
++) {
826 free(m_bound_result
[i
].buffer
);
829 free(m_bound_result
);
835 while (mysql_more_results(m_server
)) {
836 if (mysql_next_result(m_server
) != 0) {
839 MYSQL_RES
*res
= mysql_store_result(m_server
);
841 mysql_free_result(res
);
847 bool PDOMySqlStatement::create(CStrRef sql
, CArrRef options
) {
848 supports_placeholders
= PDO_PLACEHOLDER_POSITIONAL
;
851 int ret
= pdo_parse_params(this, sql
, nsql
);
853 /* query was rewritten */
854 } else if (ret
== -1) {
855 /* failed to parse */
861 if (!(m_stmt
= mysql_stmt_init(m_server
))) {
862 handleError(__FILE__
, __LINE__
);
866 if (mysql_stmt_prepare(m_stmt
, nsql
.data(), nsql
.size())) {
867 /* TODO: might need to pull statement specific info here? */
868 /* if the query isn't supported by the protocol, fallback to emulation */
869 if (mysql_errno(m_server
) == 1295) {
870 supports_placeholders
= PDO_PLACEHOLDER_NONE
;
873 handleError(__FILE__
, __LINE__
);
877 m_num_params
= mysql_stmt_param_count(m_stmt
);
880 m_params
= (MYSQL_BIND
*)calloc(m_num_params
, sizeof(MYSQL_BIND
));
881 m_in_null
= (my_bool
*)calloc(m_num_params
, sizeof(my_bool
));
882 m_in_length
= (unsigned long*)calloc(m_num_params
, sizeof(unsigned long));
885 m_max_length
= pdo_attr_lval(options
, PDO_ATTR_MAX_COLUMN_LEN
, 0);
889 bool PDOMySqlStatement::support(SupportedMethod method
) {
891 case MethodSetAttribute
:
892 case MethodGetAttribute
:
900 int PDOMySqlStatement::handleError(const char *file
, int line
) {
901 PDOMySqlConnection
*conn
= dynamic_cast<PDOMySqlConnection
*>(dbh
.get());
903 return conn
->handleError(file
, line
, this);
906 bool PDOMySqlStatement::executer() {
908 return executePrepared();
911 /* ensure that we free any previous unfetched results */
913 mysql_free_result(m_result
);
917 if (mysql_real_query(m_server
, active_query_string
.data(),
918 active_query_string
.size()) != 0) {
919 handleError(__FILE__
, __LINE__
);
923 my_ulonglong row_count
= mysql_affected_rows(m_server
);
924 if (row_count
== (my_ulonglong
)-1) {
925 /* we either have a query that returned a result set or an error occured
926 lets see if we have access to a result set */
927 if (!m_conn
->buffered()) {
928 m_result
= mysql_use_result(m_server
);
930 m_result
= mysql_store_result(m_server
);
932 if (NULL
== m_result
) {
933 handleError(__FILE__
, __LINE__
);
937 row_count
= mysql_num_rows(m_result
);
938 column_count
= (int) mysql_num_fields(m_result
);
939 m_fields
= mysql_fetch_fields(m_result
);
946 bool PDOMySqlStatement::fetcher(PDOFetchOrientation ori
, long offset
) {
949 ret
= mysql_stmt_fetch(m_stmt
);
950 if (ret
== MYSQL_DATA_TRUNCATED
) {
954 if (ret
!= MYSQL_NO_DATA
) {
955 handleError(__FILE__
, __LINE__
);
963 strcpy(error_code
, "HY000");
967 if ((m_current_data
= mysql_fetch_row(m_result
)) == NULL
) {
968 if (mysql_errno(m_server
)) {
969 handleError(__FILE__
, __LINE__
);
974 m_current_lengths
= (long int *)mysql_fetch_lengths(m_result
);
978 bool PDOMySqlStatement::describer(int colno
) {
983 if (colno
< 0 || colno
>= column_count
) {
984 /* error invalid column */
988 if (columns
.empty()) {
989 for (int i
= 0; i
< column_count
; i
++) {
990 columns
.set(i
, Resource(new PDOColumn()));
994 // fetch all on demand, this seems easiest if we've been here before bail out
995 PDOColumn
*col
= columns
[0].toResource().getTyped
<PDOColumn
>();
996 if (!col
->name
.empty()) {
999 for (int i
= 0; i
< column_count
; i
++) {
1000 col
= columns
[i
].toResource().getTyped
<PDOColumn
>();
1002 if (m_conn
->fetch_table_names()) {
1003 col
->name
= String(m_fields
[i
].table
) + "." +
1004 String(m_fields
[i
].name
);
1006 col
->name
= String(m_fields
[i
].name
, CopyString
);
1009 col
->precision
= m_fields
[i
].decimals
;
1010 col
->maxlen
= m_fields
[i
].length
;
1011 col
->param_type
= PDO_PARAM_STR
;
1016 bool PDOMySqlStatement::getColumn(int colno
, Variant
&value
) {
1022 if (m_current_data
== NULL
|| !m_result
) {
1026 if (colno
< 0 || colno
>= column_count
) {
1027 /* error invalid column */
1032 if (m_out_null
[colno
]) {
1036 ptr
= (char*)m_bound_result
[colno
].buffer
;
1037 if (m_out_length
[colno
] > m_bound_result
[colno
].buffer_length
) {
1038 /* mysql lied about the column width */
1039 strcpy(error_code
, "01004"); /* truncated */
1040 m_out_length
[colno
] = m_bound_result
[colno
].buffer_length
;
1041 len
= m_out_length
[colno
];
1042 value
= String(ptr
, len
, CopyString
);
1045 len
= m_out_length
[colno
];
1046 value
= String(ptr
, len
, CopyString
);
1049 ptr
= m_current_data
[colno
];
1050 len
= m_current_lengths
[colno
];
1051 value
= String(ptr
, len
, CopyString
);
1055 bool PDOMySqlStatement::paramHook(PDOBoundParam
*param
,
1056 PDOParamEvent event_type
) {
1058 if (m_stmt
&& param
->is_param
) {
1059 switch (event_type
) {
1060 case PDO_PARAM_EVT_ALLOC
:
1061 /* sanity check parameter number range */
1062 if (param
->paramno
< 0 || param
->paramno
>= m_num_params
) {
1063 strcpy(error_code
, "HY093");
1068 b
= &m_params
[param
->paramno
];
1069 param
->driver_data
= b
;
1070 b
->is_null
= &m_in_null
[param
->paramno
];
1071 b
->length
= &m_in_length
[param
->paramno
];
1072 /* recall how many parameters have been provided */
1075 case PDO_PARAM_EVT_EXEC_PRE
:
1076 if ((int)m_params_given
< m_num_params
) {
1077 /* too few parameter bound */
1078 strcpy(error_code
, "HY093");
1082 b
= (MYSQL_BIND
*)param
->driver_data
;
1084 if (PDO_PARAM_TYPE(param
->param_type
) == PDO_PARAM_NULL
||
1085 param
->parameter
.isNull()) {
1087 b
->buffer_type
= MYSQL_TYPE_STRING
;
1089 b
->buffer_length
= 0;
1094 switch (PDO_PARAM_TYPE(param
->param_type
)) {
1095 case PDO_PARAM_STMT
:
1098 if (param
->parameter
.isResource()) {
1099 Variant buf
= f_stream_get_contents(param
->parameter
.toResource());
1100 if (!same(buf
, false)) {
1101 param
->parameter
= buf
;
1103 pdo_raise_impl_error(dbh
, this, "HY105",
1104 "Expected a stream resource");
1114 if (param
->parameter
.isString()) {
1115 String sparam
= param
->parameter
.toString();
1116 b
->buffer_type
= MYSQL_TYPE_STRING
;
1117 b
->buffer
= (void*)sparam
.data();
1118 b
->buffer_length
= sparam
.size();
1119 *b
->length
= sparam
.size();
1122 if (param
->parameter
.isInteger()) {
1123 param
->parameter
= param
->parameter
.toInt64();
1124 b
->buffer_type
= MYSQL_TYPE_LONG
;
1125 b
->buffer
= param
->parameter
.getInt64Data();
1128 if (param
->parameter
.isDouble()) {
1129 b
->buffer_type
= MYSQL_TYPE_DOUBLE
;
1130 b
->buffer
= param
->parameter
.getDoubleData();
1134 case PDO_PARAM_EVT_FREE
:
1135 case PDO_PARAM_EVT_EXEC_POST
:
1136 case PDO_PARAM_EVT_FETCH_PRE
:
1137 case PDO_PARAM_EVT_FETCH_POST
:
1138 case PDO_PARAM_EVT_NORMALIZE
:
1146 static const StaticString
s_mysql_def("mysql:def");
1147 static const StaticString
s_not_null("not_null");
1148 static const StaticString
s_primary_key("primary_key");
1149 static const StaticString
s_multiple_key("multiple_key");
1150 static const StaticString
s_unique_key("unique_key");
1151 static const StaticString
s_blob("blob");
1152 static const StaticString
s_native_type("native_type");
1153 static const StaticString
s_flags("flags");
1154 static const StaticString
s_table("table");
1156 bool PDOMySqlStatement::getColumnMeta(int64_t colno
, Array
&ret
) {
1160 if (colno
< 0 || colno
>= column_count
) {
1161 /* error invalid column */
1165 Array flags
= Array::Create();
1167 const MYSQL_FIELD
*F
= m_fields
+ colno
;
1169 ret
.set(s_mysql_def
, String(F
->def
, CopyString
));
1171 if (IS_NOT_NULL(F
->flags
)) {
1172 flags
.append(s_not_null
);
1174 if (IS_PRI_KEY(F
->flags
)) {
1175 flags
.append(s_primary_key
);
1177 if (F
->flags
& MULTIPLE_KEY_FLAG
) {
1178 flags
.append(s_multiple_key
);
1180 if (F
->flags
& UNIQUE_KEY_FLAG
) {
1181 flags
.append(s_unique_key
);
1183 if (IS_BLOB(F
->flags
)) {
1184 flags
.append(s_blob
);
1186 const char *str
= type_to_name_native(F
->type
);
1188 ret
.set(s_native_type
, str
);
1190 ret
.set(s_flags
, flags
);
1191 ret
.set(s_table
, String(F
->table
, CopyString
));
1195 bool PDOMySqlStatement::nextRowset() {
1196 /* ensure that we free any previous unfetched results */
1198 column_count
= (int)mysql_num_fields(m_result
);
1199 mysql_stmt_free_result(m_stmt
);
1202 mysql_free_result(m_result
);
1206 int ret
= mysql_next_result(m_server
);
1208 handleError(__FILE__
, __LINE__
);
1212 /* No more results */
1216 my_ulonglong row_count
;
1217 if (!m_conn
->buffered()) {
1218 m_result
= mysql_use_result(m_server
);
1221 m_result
= mysql_store_result(m_server
);
1222 if ((my_ulonglong
)-1 == (row_count
= mysql_affected_rows(m_server
))) {
1223 handleError(__FILE__
, __LINE__
);
1232 column_count
= (int)mysql_num_fields(m_result
);
1233 m_fields
= mysql_fetch_fields(m_result
);
1237 bool PDOMySqlStatement::cursorCloser() {
1239 mysql_free_result(m_result
);
1243 return !mysql_stmt_free_result(m_stmt
);
1246 while (mysql_more_results(m_server
)) {
1247 if (mysql_next_result(m_server
) != 0) {
1250 MYSQL_RES
*res
= mysql_store_result(m_server
);
1252 mysql_free_result(res
);
1258 ///////////////////////////////////////////////////////////////////////////////
1260 PDOMySql::PDOMySql() : PDODriver("mysql") {
1263 PDOConnection
*PDOMySql::createConnectionObject() {
1264 return new PDOMySqlConnection();
1267 ///////////////////////////////////////////////////////////////////////////////