84b9d9a3a2
In HHVM (and HPHPc before it) we've been piggybacking resources on the KindOfObject machinery. At the language level, resource is considered to be a different type than object, and there are a number of differences in behavior between objects and resources (ex. resources don't allow for dynamic properties, resources don't work with the clone operator, the "(object)" cast behaves differently for resources vs. objects, etc). Piggybacking resources on the KindOfObject machinery has some downsides. Code that deals with KindOfObject values often needs to check if the value is a resource and go down a different code path. This makes things harder to maintain and harder to keep parity with Zend. Also, these extra branches hurt performance a little, and they make it harder for the JIT to do a good job in some cases when its generating machine code that operates on objects. This diff prepares the code base for a new KindOfResource type by adding a new "Resource" smart pointer type (currently a typedef for the Object smart pointer type) and it updates the C++ code and the idl files appropriately. This diff is essentially a cosmetic change and should not impact run time behavior. In the next diff (part 2) we'll actually add a new KindOfResource type, detach ResourceData from the ObjectData inheritence hierarchy, and provide a real implementation for the Resource smart pointer type (instead of just aliasing the Object smart pointer type).
1269 linhas
34 KiB
C++
1269 linhas
34 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
|
| Copyright (c) 1997-2010 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "hphp/runtime/ext/pdo_mysql.h"
|
|
#include "hphp/runtime/ext/ext_stream.h"
|
|
#include "mysql.h"
|
|
|
|
#ifdef PHP_MYSQL_UNIX_SOCK_ADDR
|
|
#ifdef MYSQL_UNIX_ADDR
|
|
#undef MYSQL_UNIX_ADDR
|
|
#endif
|
|
#define MYSQL_UNIX_ADDR PHP_MYSQL_UNIX_SOCK_ADDR
|
|
#endif
|
|
|
|
namespace HPHP {
|
|
IMPLEMENT_DEFAULT_EXTENSION(pdo_mysql);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
class PDOMySqlError {
|
|
public:
|
|
PDOMySqlError() : file(NULL), line(0), errcode(0), errmsg(NULL) {
|
|
}
|
|
|
|
const char *file;
|
|
int line;
|
|
unsigned int errcode;
|
|
char *errmsg;
|
|
};
|
|
|
|
class PDOMySqlStatement;
|
|
class PDOMySqlConnection : public PDOConnection {
|
|
public:
|
|
PDOMySqlConnection();
|
|
virtual ~PDOMySqlConnection();
|
|
virtual bool create(CArrRef options);
|
|
|
|
int handleError(const char *file, int line, PDOMySqlStatement *stmt = NULL);
|
|
|
|
virtual bool support(SupportedMethod method);
|
|
virtual bool closer();
|
|
virtual bool preparer(CStrRef sql, sp_PDOStatement *stmt, CVarRef options);
|
|
virtual int64_t doer(CStrRef sql);
|
|
virtual bool quoter(CStrRef input, String "ed, PDOParamType paramtype);
|
|
virtual bool begin();
|
|
virtual bool commit();
|
|
virtual bool rollback();
|
|
virtual bool setAttribute(int64_t attr, CVarRef value);
|
|
virtual String lastId(const char *name);
|
|
virtual bool fetchErr(PDOStatement *stmt, Array &info);
|
|
virtual int getAttribute(int64_t attr, Variant &value);
|
|
virtual bool checkLiveness();
|
|
virtual void persistentShutdown();
|
|
|
|
bool buffered() const { return m_buffered;}
|
|
unsigned long max_buffer_size() const { return m_max_buffer_size;}
|
|
bool fetch_table_names() const { return m_fetch_table_names;}
|
|
|
|
private:
|
|
MYSQL *m_server;
|
|
unsigned m_attached:1;
|
|
unsigned m_buffered:1;
|
|
unsigned m_emulate_prepare:1;
|
|
unsigned m_fetch_table_names:1;
|
|
unsigned long m_max_buffer_size;
|
|
PDOMySqlError m_einfo;
|
|
};
|
|
|
|
class PDOMySqlStatement : public PDOStatement {
|
|
public:
|
|
PDOMySqlStatement(PDOMySqlConnection *conn, MYSQL *server);
|
|
virtual ~PDOMySqlStatement();
|
|
|
|
bool create(CStrRef sql, CArrRef options);
|
|
|
|
virtual bool support(SupportedMethod method);
|
|
virtual bool executer();
|
|
virtual bool fetcher(PDOFetchOrientation ori, long offset);
|
|
virtual bool describer(int colno);
|
|
virtual bool getColumn(int colno, Variant &value);
|
|
virtual bool paramHook(PDOBoundParam *param, PDOParamEvent event_type);
|
|
virtual bool getColumnMeta(int64_t colno, Array &return_value);
|
|
virtual bool nextRowset();
|
|
virtual bool cursorCloser();
|
|
|
|
MYSQL_STMT *stmt() { return m_stmt;}
|
|
|
|
private:
|
|
PDOMySqlConnection *m_conn;
|
|
MYSQL *m_server;
|
|
MYSQL_RES *m_result;
|
|
const MYSQL_FIELD *m_fields;
|
|
MYSQL_ROW m_current_data;
|
|
long *m_current_lengths;
|
|
PDOMySqlError m_einfo;
|
|
MYSQL_STMT *m_stmt;
|
|
int m_num_params;
|
|
MYSQL_BIND *m_params;
|
|
my_bool *m_in_null;
|
|
unsigned long *m_in_length;
|
|
MYSQL_BIND *m_bound_result;
|
|
my_bool *m_out_null;
|
|
unsigned long *m_out_length;
|
|
unsigned int m_params_given;
|
|
unsigned m_max_length:1;
|
|
|
|
void setRowCount();
|
|
bool executePrepared();
|
|
int handleError(const char *file, int line);
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/* For the convenience of drivers, this function will parse a data source
|
|
* string, of the form "name=value; name2=value2" and populate variables
|
|
* according to the data you pass in and array of pdo_data_src_parser structures */
|
|
struct pdo_data_src_parser {
|
|
const char *optname;
|
|
char *optval;
|
|
int freeme;
|
|
};
|
|
|
|
static int php_pdo_parse_data_source(const char *data_source,
|
|
int data_source_len,
|
|
struct pdo_data_src_parser *parsed,
|
|
int nparams) {
|
|
int i, j;
|
|
int valstart = -1;
|
|
int semi = -1;
|
|
int optstart = 0;
|
|
int nlen;
|
|
int n_matches = 0;
|
|
|
|
i = 0;
|
|
while (i < data_source_len) {
|
|
/* looking for NAME= */
|
|
|
|
if (data_source[i] == '\0') {
|
|
break;
|
|
}
|
|
|
|
if (data_source[i] != '=') {
|
|
++i;
|
|
continue;
|
|
}
|
|
|
|
valstart = ++i;
|
|
|
|
/* now we're looking for VALUE; or just VALUE<NUL> */
|
|
semi = -1;
|
|
while (i < data_source_len) {
|
|
if (data_source[i] == '\0') {
|
|
semi = i++;
|
|
break;
|
|
}
|
|
if (data_source[i] == ';') {
|
|
semi = i++;
|
|
break;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (semi == -1) {
|
|
semi = i;
|
|
}
|
|
|
|
/* find the entry in the array */
|
|
nlen = valstart - optstart - 1;
|
|
for (j = 0; j < nparams; j++) {
|
|
if (0 == strncmp(data_source + optstart, parsed[j].optname, nlen) &&
|
|
parsed[j].optname[nlen] == '\0') {
|
|
/* got a match */
|
|
if (parsed[j].freeme) {
|
|
free(parsed[j].optval);
|
|
}
|
|
parsed[j].optval = strndup(data_source + valstart, semi - valstart);
|
|
parsed[j].freeme = 1;
|
|
++n_matches;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (i < data_source_len && isspace(data_source[i])) {
|
|
i++;
|
|
}
|
|
|
|
optstart = i;
|
|
}
|
|
|
|
return n_matches;
|
|
}
|
|
|
|
static long pdo_attr_lval(CArrRef options, int opt, long defaultValue) {
|
|
if (options.exists(opt)) {
|
|
return options[opt].toInt64();
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
static String pdo_attr_strval(CArrRef options, int opt, const char *def) {
|
|
if (options.exists(opt)) {
|
|
return options[opt].toString();
|
|
}
|
|
if (def) {
|
|
return def;
|
|
}
|
|
return String();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
PDOMySqlConnection::PDOMySqlConnection()
|
|
: m_server(NULL), m_attached(0), m_buffered(0), m_emulate_prepare(0),
|
|
m_fetch_table_names(0), m_max_buffer_size(0) {
|
|
}
|
|
|
|
PDOMySqlConnection::~PDOMySqlConnection() {
|
|
if (m_server) {
|
|
mysql_close(m_server);
|
|
}
|
|
if (m_einfo.errmsg) {
|
|
free(m_einfo.errmsg);
|
|
}
|
|
}
|
|
|
|
bool PDOMySqlConnection::create(CArrRef options) {
|
|
int i, ret = 0;
|
|
char *host = NULL, *unix_socket = NULL;
|
|
unsigned int port = 3306;
|
|
char *dbname;
|
|
struct pdo_data_src_parser vars[] = {
|
|
{ "charset", NULL, 0 },
|
|
{ "dbname", "", 0 },
|
|
{ "host", "localhost", 0 },
|
|
{ "port", "3306", 0 },
|
|
{ "unix_socket", MYSQL_UNIX_ADDR, 0 },
|
|
};
|
|
int connect_opts = 0
|
|
#ifdef CLIENT_MULTI_RESULTS
|
|
|CLIENT_MULTI_RESULTS
|
|
#endif
|
|
#ifdef CLIENT_MULTI_STATEMENTS
|
|
|CLIENT_MULTI_STATEMENTS
|
|
#endif
|
|
;
|
|
|
|
php_pdo_parse_data_source(data_source.data(), data_source.size(), vars, 5);
|
|
|
|
/* handle for the server */
|
|
if (!(m_server = mysql_init(NULL))) {
|
|
handleError(__FILE__, __LINE__);
|
|
goto cleanup;
|
|
}
|
|
|
|
m_max_buffer_size = 1024*1024;
|
|
m_buffered = m_emulate_prepare = 1;
|
|
|
|
/* handle MySQL options */
|
|
if (!options.empty()) {
|
|
long connect_timeout = pdo_attr_lval(options, PDO_ATTR_TIMEOUT, 30);
|
|
long local_infile = pdo_attr_lval(options, PDO_MYSQL_ATTR_LOCAL_INFILE, 0);
|
|
String init_cmd, default_file, default_group;
|
|
long compress = 0;
|
|
m_buffered = pdo_attr_lval(options, PDO_MYSQL_ATTR_USE_BUFFERED_QUERY, 1);
|
|
|
|
m_emulate_prepare = pdo_attr_lval(options, PDO_MYSQL_ATTR_DIRECT_QUERY,
|
|
m_emulate_prepare);
|
|
m_emulate_prepare = pdo_attr_lval(options, PDO_ATTR_EMULATE_PREPARES,
|
|
m_emulate_prepare);
|
|
|
|
m_max_buffer_size = pdo_attr_lval(options, PDO_MYSQL_ATTR_MAX_BUFFER_SIZE,
|
|
m_max_buffer_size);
|
|
|
|
if (pdo_attr_lval(options, PDO_MYSQL_ATTR_FOUND_ROWS, 0)) {
|
|
connect_opts |= CLIENT_FOUND_ROWS;
|
|
}
|
|
if (pdo_attr_lval(options, PDO_MYSQL_ATTR_IGNORE_SPACE, 0)) {
|
|
connect_opts |= CLIENT_IGNORE_SPACE;
|
|
}
|
|
|
|
if (mysql_options(m_server, MYSQL_OPT_CONNECT_TIMEOUT,
|
|
(const char *)&connect_timeout)) {
|
|
handleError(__FILE__, __LINE__);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (mysql_options(m_server, MYSQL_OPT_LOCAL_INFILE,
|
|
(const char *)&local_infile)) {
|
|
handleError(__FILE__, __LINE__);
|
|
goto cleanup;
|
|
}
|
|
#ifdef MYSQL_OPT_RECONNECT
|
|
/* since 5.0.3, the default for this option is 0 if not specified.
|
|
* we want the old behaviour */
|
|
{
|
|
long reconnect = 1;
|
|
mysql_options(m_server, MYSQL_OPT_RECONNECT, (const char*)&reconnect);
|
|
}
|
|
#endif
|
|
init_cmd = pdo_attr_strval(options, PDO_MYSQL_ATTR_INIT_COMMAND, NULL);
|
|
if (!init_cmd.empty()) {
|
|
if (mysql_options(m_server, MYSQL_INIT_COMMAND, init_cmd.data())) {
|
|
handleError(__FILE__, __LINE__);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
default_file = pdo_attr_strval(options, PDO_MYSQL_ATTR_READ_DEFAULT_FILE,
|
|
NULL);
|
|
if (!default_file.empty()) {
|
|
if (mysql_options(m_server, MYSQL_READ_DEFAULT_FILE,
|
|
default_file.data())) {
|
|
handleError(__FILE__, __LINE__);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
default_group = pdo_attr_strval(options, PDO_MYSQL_ATTR_READ_DEFAULT_GROUP,
|
|
NULL);
|
|
if (!default_group.empty()) {
|
|
if (mysql_options(m_server, MYSQL_READ_DEFAULT_GROUP,
|
|
default_group.data())) {
|
|
handleError(__FILE__, __LINE__);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
compress = pdo_attr_lval(options, PDO_MYSQL_ATTR_COMPRESS, 0);
|
|
if (compress) {
|
|
if (mysql_options(m_server, MYSQL_OPT_COMPRESS, 0)) {
|
|
handleError(__FILE__, __LINE__);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
dbname = vars[1].optval;
|
|
host = vars[2].optval;
|
|
if (vars[3].optval) {
|
|
port = atoi(vars[3].optval);
|
|
}
|
|
if (vars[2].optval && !strcmp("localhost", vars[2].optval)) {
|
|
unix_socket = vars[4].optval;
|
|
}
|
|
|
|
/* TODO: - Check zval cache + ZTS */
|
|
if (mysql_real_connect(m_server, host, username.c_str(), password.c_str(),
|
|
dbname, port, unix_socket, connect_opts) == NULL) {
|
|
handleError(__FILE__, __LINE__);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!auto_commit) {
|
|
mysql_autocommit(m_server, auto_commit);
|
|
}
|
|
|
|
m_attached = 1;
|
|
|
|
alloc_own_columns = 1;
|
|
max_escaped_char_length = 2;
|
|
|
|
ret = 1;
|
|
|
|
cleanup:
|
|
for (i = 0; i < (int)(sizeof(vars)/sizeof(vars[0])); i++) {
|
|
if (vars[i].freeme) {
|
|
free(vars[i].optval);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool PDOMySqlConnection::support(SupportedMethod method) {
|
|
return true;
|
|
}
|
|
|
|
bool PDOMySqlConnection::closer() {
|
|
if (m_server) {
|
|
mysql_close(m_server);
|
|
m_server = NULL;
|
|
}
|
|
if (m_einfo.errmsg) {
|
|
free(m_einfo.errmsg);
|
|
m_einfo.errmsg = NULL;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int PDOMySqlConnection::handleError(const char *file, int line,
|
|
PDOMySqlStatement *stmt) {
|
|
PDOErrorType *pdo_err;
|
|
PDOMySqlError *einfo = &m_einfo;
|
|
|
|
if (stmt) {
|
|
pdo_err = &stmt->error_code;
|
|
} else {
|
|
pdo_err = &error_code;
|
|
}
|
|
|
|
if (stmt && stmt->stmt()) {
|
|
einfo->errcode = mysql_stmt_errno(stmt->stmt());
|
|
} else {
|
|
einfo->errcode = mysql_errno(m_server);
|
|
}
|
|
|
|
einfo->file = file;
|
|
einfo->line = line;
|
|
|
|
if (einfo->errmsg) {
|
|
free(einfo->errmsg);
|
|
einfo->errmsg = NULL;
|
|
}
|
|
|
|
if (einfo->errcode) {
|
|
if (einfo->errcode == 2014) {
|
|
einfo->errmsg =
|
|
strdup("Cannot execute queries while other unbuffered queries are "
|
|
"active. Consider using PDOStatement::fetchAll(). "
|
|
"Alternatively, if your code is only ever going to run against "
|
|
"mysql, you may enable query buffering by setting the "
|
|
"PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.");
|
|
} else if (einfo->errcode == 2057) {
|
|
einfo->errmsg =
|
|
strdup("A stored procedure returning result sets of different size "
|
|
"was called. This is not supported by libmysql");
|
|
} else {
|
|
einfo->errmsg = strdup(mysql_error(m_server));
|
|
}
|
|
} else { /* no error */
|
|
strcpy(*pdo_err, PDO_ERR_NONE);
|
|
return false;
|
|
}
|
|
|
|
if (stmt && stmt->stmt()) {
|
|
strcpy(*pdo_err, mysql_stmt_sqlstate(stmt->stmt()));
|
|
} else {
|
|
strcpy(*pdo_err, mysql_sqlstate(m_server));
|
|
}
|
|
|
|
if (stmt && stmt->stmt()) {
|
|
pdo_raise_impl_error(stmt->dbh, NULL, pdo_err[0], einfo->errmsg);
|
|
} else {
|
|
throw_pdo_exception((int)einfo->errcode, uninit_null(), "SQLSTATE[%s] [%d] %s",
|
|
pdo_err[0], einfo->errcode, einfo->errmsg);
|
|
}
|
|
return einfo->errcode;
|
|
}
|
|
|
|
bool PDOMySqlConnection::preparer(CStrRef sql, sp_PDOStatement *stmt,
|
|
CVarRef options) {
|
|
PDOMySqlStatement *s = new PDOMySqlStatement(this, m_server);
|
|
*stmt = s;
|
|
|
|
if (m_emulate_prepare) {
|
|
return true;
|
|
}
|
|
int server_version = mysql_get_server_version(m_server);
|
|
if (server_version < 40100) {
|
|
return true;
|
|
}
|
|
|
|
if (s->create(sql, options.toArray())) {
|
|
alloc_own_columns = 1;
|
|
return true;
|
|
}
|
|
|
|
stmt->reset();
|
|
strcpy(error_code, s->error_code);
|
|
return false;
|
|
}
|
|
|
|
int64_t PDOMySqlConnection::doer(CStrRef sql) {
|
|
if (mysql_real_query(m_server, sql.data(), sql.size())) {
|
|
handleError(__FILE__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
my_ulonglong c = mysql_affected_rows(m_server);
|
|
if (c == (my_ulonglong) -1) {
|
|
handleError(__FILE__, __LINE__);
|
|
return m_einfo.errcode ? -1 : 0;
|
|
}
|
|
|
|
/* MULTI_QUERY support - eat up all unfetched result sets */
|
|
while (mysql_more_results(m_server)) {
|
|
if (mysql_next_result(m_server)) {
|
|
return true;
|
|
}
|
|
MYSQL_RES *result = mysql_store_result(m_server);
|
|
if (result) {
|
|
mysql_free_result(result);
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
bool PDOMySqlConnection::quoter(CStrRef input, String "ed,
|
|
PDOParamType paramtype) {
|
|
String s(2 * input.size() + 3, ReserveString);
|
|
char *buf = s.mutableSlice().ptr;
|
|
int len = mysql_real_escape_string(m_server, buf + 1,
|
|
input.data(), input.size());
|
|
len++;
|
|
buf[0] = buf[len] = '\'';
|
|
len++;
|
|
quoted = s.setSize(len);
|
|
return true;
|
|
}
|
|
|
|
bool PDOMySqlConnection::begin() {
|
|
return doer("START TRANSACTION") >= 0;
|
|
}
|
|
|
|
bool PDOMySqlConnection::commit() {
|
|
return mysql_commit(m_server) >= 0;
|
|
}
|
|
|
|
bool PDOMySqlConnection::rollback() {
|
|
return mysql_rollback(m_server) >= 0;
|
|
}
|
|
|
|
bool PDOMySqlConnection::setAttribute(int64_t attr, CVarRef value) {
|
|
switch (attr) {
|
|
case PDO_ATTR_AUTOCOMMIT:
|
|
/* ignore if the new value equals the old one */
|
|
if (auto_commit ^ value.toBoolean()) {
|
|
auto_commit = value.toBoolean();
|
|
mysql_autocommit(m_server, auto_commit);
|
|
}
|
|
return true;
|
|
|
|
case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY:
|
|
m_buffered = value.toBoolean();
|
|
return true;
|
|
case PDO_MYSQL_ATTR_DIRECT_QUERY:
|
|
case PDO_ATTR_EMULATE_PREPARES:
|
|
m_emulate_prepare = value.toBoolean();
|
|
return true;
|
|
case PDO_ATTR_FETCH_TABLE_NAMES:
|
|
m_fetch_table_names = value.toBoolean();
|
|
return true;
|
|
case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE:
|
|
if (value.toInt64() < 0) {
|
|
/* TODO: Johannes, can we throw a warning here? */
|
|
m_max_buffer_size = 1024*1024;
|
|
} else {
|
|
m_max_buffer_size = value.toInt64();
|
|
}
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
String PDOMySqlConnection::lastId(const char *name) {
|
|
return (int64_t)mysql_insert_id(m_server);
|
|
}
|
|
|
|
bool PDOMySqlConnection::fetchErr(PDOStatement *stmt, Array &info) {
|
|
if (m_einfo.errcode) {
|
|
info.append((int64_t)m_einfo.errcode);
|
|
info.append(String(m_einfo.errmsg, CopyString));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int PDOMySqlConnection::getAttribute(int64_t attr, Variant &value) {
|
|
switch (attr) {
|
|
case PDO_ATTR_CLIENT_VERSION:
|
|
value = String((char *)mysql_get_client_info(), CopyString);
|
|
break;
|
|
case PDO_ATTR_SERVER_VERSION:
|
|
value = String((char *)mysql_get_server_info(m_server), CopyString);
|
|
break;
|
|
case PDO_ATTR_CONNECTION_STATUS:
|
|
value = String((char *)mysql_get_host_info(m_server), CopyString);
|
|
break;
|
|
case PDO_ATTR_SERVER_INFO: {
|
|
char *tmp = (char *)mysql_stat(m_server);
|
|
if (tmp) {
|
|
value = String(tmp, CopyString);
|
|
} else {
|
|
handleError(__FILE__, __LINE__);
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
case PDO_ATTR_AUTOCOMMIT:
|
|
value = (int64_t)auto_commit;
|
|
break;
|
|
case PDO_MYSQL_ATTR_USE_BUFFERED_QUERY:
|
|
value = (int64_t)m_buffered;
|
|
break;
|
|
case PDO_MYSQL_ATTR_DIRECT_QUERY:
|
|
value = (int64_t)m_emulate_prepare;
|
|
break;
|
|
case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE:
|
|
value = (int64_t)m_max_buffer_size;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
bool PDOMySqlConnection::checkLiveness() {
|
|
return !mysql_ping(m_server);
|
|
}
|
|
|
|
void PDOMySqlConnection::persistentShutdown() {
|
|
// do nothing
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void PDOMySqlStatement::setRowCount() {
|
|
my_ulonglong count = mysql_stmt_affected_rows(m_stmt);
|
|
if (count != (my_ulonglong)-1) {
|
|
row_count = count;
|
|
}
|
|
}
|
|
|
|
bool PDOMySqlStatement::executePrepared() {
|
|
/* (re)bind the parameters */
|
|
if (mysql_stmt_bind_param(m_stmt, m_params) || mysql_stmt_execute(m_stmt)) {
|
|
if (m_params) {
|
|
free(m_params);
|
|
m_params = 0;
|
|
}
|
|
handleError(__FILE__, __LINE__);
|
|
if (mysql_stmt_errno(m_stmt) == 2057) {
|
|
/* CR_NEW_STMT_METADATA makes the statement unusable */
|
|
m_stmt = NULL;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (!m_result) {
|
|
int i;
|
|
|
|
/* figure out the result set format, if any */
|
|
m_result = mysql_stmt_result_metadata(m_stmt);
|
|
if (m_result) {
|
|
int calc_max_length = m_conn->buffered() && m_max_length == 1;
|
|
m_fields = mysql_fetch_fields(m_result);
|
|
if (m_bound_result) {
|
|
int i;
|
|
for (i = 0; i < column_count; i++) {
|
|
free(m_bound_result[i].buffer);
|
|
}
|
|
free(m_bound_result);
|
|
free(m_out_null);
|
|
free(m_out_length);
|
|
}
|
|
|
|
column_count = (int)mysql_num_fields(m_result);
|
|
m_bound_result = (MYSQL_BIND*)calloc(column_count, sizeof(MYSQL_BIND));
|
|
m_out_null = (my_bool*)calloc(column_count, sizeof(my_bool));
|
|
m_out_length = (unsigned long *)calloc(column_count,
|
|
sizeof(unsigned long));
|
|
|
|
/* summon memory to hold the row */
|
|
for (i = 0; i < column_count; i++) {
|
|
if (calc_max_length && m_fields[i].type == FIELD_TYPE_BLOB) {
|
|
my_bool on = 1;
|
|
mysql_stmt_attr_set(m_stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &on);
|
|
calc_max_length = 0;
|
|
}
|
|
switch (m_fields[i].type) {
|
|
case FIELD_TYPE_INT24:
|
|
m_bound_result[i].buffer_length = MAX_MEDIUMINT_WIDTH + 1;
|
|
break;
|
|
case FIELD_TYPE_LONG:
|
|
m_bound_result[i].buffer_length = MAX_INT_WIDTH + 1;
|
|
break;
|
|
case FIELD_TYPE_LONGLONG:
|
|
m_bound_result[i].buffer_length = MAX_BIGINT_WIDTH + 1;
|
|
break;
|
|
case FIELD_TYPE_TINY:
|
|
m_bound_result[i].buffer_length = MAX_TINYINT_WIDTH + 1;
|
|
break;
|
|
case FIELD_TYPE_SHORT:
|
|
m_bound_result[i].buffer_length = MAX_SMALLINT_WIDTH + 1;
|
|
break;
|
|
default:
|
|
m_bound_result[i].buffer_length =
|
|
m_fields[i].max_length? m_fields[i].max_length:
|
|
m_fields[i].length;
|
|
/* work-around for longtext and alike */
|
|
if (m_bound_result[i].buffer_length > m_conn->max_buffer_size()) {
|
|
m_bound_result[i].buffer_length = m_conn->max_buffer_size();
|
|
}
|
|
}
|
|
|
|
/* there are cases where the length reported by mysql is too short.
|
|
* eg: when describing a table that contains an enum column. Since
|
|
* we have no way of knowing the true length either, we'll bump up
|
|
* our buffer size to a reasonable size, just in case */
|
|
if (m_fields[i].max_length == 0 &&
|
|
m_bound_result[i].buffer_length < 128 && MYSQL_TYPE_VAR_STRING) {
|
|
m_bound_result[i].buffer_length = 128;
|
|
}
|
|
|
|
m_out_length[i] = 0;
|
|
|
|
m_bound_result[i].buffer = malloc(m_bound_result[i].buffer_length);
|
|
m_bound_result[i].is_null = &m_out_null[i];
|
|
m_bound_result[i].length = &m_out_length[i];
|
|
m_bound_result[i].buffer_type = MYSQL_TYPE_STRING;
|
|
}
|
|
|
|
if (mysql_stmt_bind_result(m_stmt, m_bound_result)) {
|
|
handleError(__FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
|
|
/* if buffered, pre-fetch all the data */
|
|
if (m_conn->buffered()) {
|
|
mysql_stmt_store_result(m_stmt);
|
|
}
|
|
}
|
|
}
|
|
|
|
setRowCount();
|
|
return true;
|
|
}
|
|
|
|
static const char *type_to_name_native(int type) {
|
|
#define PDO_MYSQL_NATIVE_TYPE_NAME(x) case FIELD_TYPE_##x: return #x;
|
|
|
|
switch (type) {
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(STRING)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(VAR_STRING)
|
|
#ifdef MYSQL_HAS_TINY
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(TINY)
|
|
#endif
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(SHORT)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(LONG)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(LONGLONG)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(INT24)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(FLOAT)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(DOUBLE)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(DECIMAL)
|
|
#ifdef FIELD_TYPE_NEWDECIMAL
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(NEWDECIMAL)
|
|
#endif
|
|
#ifdef FIELD_TYPE_GEOMETRY
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(GEOMETRY)
|
|
#endif
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(TIMESTAMP)
|
|
#ifdef MYSQL_HAS_YEAR
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(YEAR)
|
|
#endif
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(SET)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(ENUM)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(DATE)
|
|
#ifdef FIELD_TYPE_NEWDATE
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(NEWDATE)
|
|
#endif
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(TIME)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(DATETIME)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(TINY_BLOB)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(MEDIUM_BLOB)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(LONG_BLOB)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(BLOB)
|
|
PDO_MYSQL_NATIVE_TYPE_NAME(NULL)
|
|
default:
|
|
return NULL;
|
|
}
|
|
#undef PDO_MYSQL_NATIVE_TYPE_NAME
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
PDOMySqlStatement::PDOMySqlStatement(PDOMySqlConnection *conn, MYSQL *server)
|
|
: m_conn(conn), m_server(server), m_result(NULL), m_fields(NULL),
|
|
m_current_data(NULL), m_current_lengths(NULL), m_stmt(NULL),
|
|
m_num_params(0), m_params(NULL), m_in_null(NULL), m_in_length(NULL),
|
|
m_bound_result(NULL), m_out_null(NULL), m_out_length(NULL),
|
|
m_params_given(0), m_max_length(0) {
|
|
}
|
|
|
|
PDOMySqlStatement::~PDOMySqlStatement() {
|
|
if (m_result) {
|
|
/* free the resource */
|
|
mysql_free_result(m_result);
|
|
m_result = NULL;
|
|
}
|
|
if (m_einfo.errmsg) {
|
|
free(m_einfo.errmsg);
|
|
m_einfo.errmsg = NULL;
|
|
}
|
|
if (m_stmt) {
|
|
mysql_stmt_close(m_stmt);
|
|
m_stmt = NULL;
|
|
}
|
|
|
|
if (m_params) {
|
|
free(m_params);
|
|
}
|
|
if (m_in_null) {
|
|
free(m_in_null);
|
|
}
|
|
if (m_in_length) {
|
|
free(m_in_length);
|
|
}
|
|
|
|
if (m_bound_result) {
|
|
int i;
|
|
for (i = 0; i < column_count; i++) {
|
|
free(m_bound_result[i].buffer);
|
|
}
|
|
|
|
free(m_bound_result);
|
|
free(m_out_null);
|
|
free(m_out_length);
|
|
}
|
|
|
|
if (m_server) {
|
|
while (mysql_more_results(m_server)) {
|
|
if (mysql_next_result(m_server) != 0) {
|
|
break;
|
|
}
|
|
MYSQL_RES *res = mysql_store_result(m_server);
|
|
if (res) {
|
|
mysql_free_result(res);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PDOMySqlStatement::create(CStrRef sql, CArrRef options) {
|
|
supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
|
|
|
|
String nsql;
|
|
int ret = pdo_parse_params(this, sql, nsql);
|
|
if (ret == 1) {
|
|
/* query was rewritten */
|
|
} else if (ret == -1) {
|
|
/* failed to parse */
|
|
return false;
|
|
} else {
|
|
nsql = sql;
|
|
}
|
|
|
|
if (!(m_stmt = mysql_stmt_init(m_server))) {
|
|
handleError(__FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
|
|
if (mysql_stmt_prepare(m_stmt, nsql.data(), nsql.size())) {
|
|
/* TODO: might need to pull statement specific info here? */
|
|
/* if the query isn't supported by the protocol, fallback to emulation */
|
|
if (mysql_errno(m_server) == 1295) {
|
|
supports_placeholders = PDO_PLACEHOLDER_NONE;
|
|
return true;
|
|
}
|
|
handleError(__FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
|
|
m_num_params = mysql_stmt_param_count(m_stmt);
|
|
if (m_num_params) {
|
|
m_params_given = 0;
|
|
m_params = (MYSQL_BIND*)calloc(m_num_params, sizeof(MYSQL_BIND));
|
|
m_in_null = (my_bool*)calloc(m_num_params, sizeof(my_bool));
|
|
m_in_length = (unsigned long*)calloc(m_num_params, sizeof(unsigned long));
|
|
}
|
|
|
|
m_max_length = pdo_attr_lval(options, PDO_ATTR_MAX_COLUMN_LEN, 0);
|
|
return true;
|
|
}
|
|
|
|
bool PDOMySqlStatement::support(SupportedMethod method) {
|
|
switch (method) {
|
|
case MethodSetAttribute:
|
|
case MethodGetAttribute:
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int PDOMySqlStatement::handleError(const char *file, int line) {
|
|
PDOMySqlConnection *conn = dynamic_cast<PDOMySqlConnection*>(dbh.get());
|
|
assert(conn);
|
|
return conn->handleError(file, line, this);
|
|
}
|
|
|
|
bool PDOMySqlStatement::executer() {
|
|
if (m_stmt) {
|
|
return executePrepared();
|
|
}
|
|
|
|
/* ensure that we free any previous unfetched results */
|
|
if (m_result) {
|
|
mysql_free_result(m_result);
|
|
m_result = NULL;
|
|
}
|
|
|
|
if (mysql_real_query(m_server, active_query_string.data(),
|
|
active_query_string.size()) != 0) {
|
|
handleError(__FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
|
|
my_ulonglong row_count = mysql_affected_rows(m_server);
|
|
if (row_count == (my_ulonglong)-1) {
|
|
/* we either have a query that returned a result set or an error occured
|
|
lets see if we have access to a result set */
|
|
if (!m_conn->buffered()) {
|
|
m_result = mysql_use_result(m_server);
|
|
} else {
|
|
m_result = mysql_store_result(m_server);
|
|
}
|
|
if (NULL == m_result) {
|
|
handleError(__FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
|
|
row_count = mysql_num_rows(m_result);
|
|
column_count = (int) mysql_num_fields(m_result);
|
|
m_fields = mysql_fetch_fields(m_result);
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PDOMySqlStatement::fetcher(PDOFetchOrientation ori, long offset) {
|
|
int ret;
|
|
if (m_stmt) {
|
|
ret = mysql_stmt_fetch(m_stmt);
|
|
if (ret == MYSQL_DATA_TRUNCATED) {
|
|
ret = 0;
|
|
}
|
|
if (ret) {
|
|
if (ret != MYSQL_NO_DATA) {
|
|
handleError(__FILE__, __LINE__);
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (!m_result) {
|
|
strcpy(error_code, "HY000");
|
|
return false;
|
|
}
|
|
|
|
if ((m_current_data = mysql_fetch_row(m_result)) == NULL) {
|
|
if (mysql_errno(m_server)) {
|
|
handleError(__FILE__, __LINE__);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
m_current_lengths = (long int *)mysql_fetch_lengths(m_result);
|
|
return true;
|
|
}
|
|
|
|
bool PDOMySqlStatement::describer(int colno) {
|
|
if (!m_result) {
|
|
return false;
|
|
}
|
|
|
|
if (colno < 0 || colno >= column_count) {
|
|
/* error invalid column */
|
|
return false;
|
|
}
|
|
|
|
if (columns.empty()) {
|
|
for (int i = 0; i < column_count; i++) {
|
|
columns.set(i, Resource(new PDOColumn()));
|
|
}
|
|
}
|
|
|
|
// fetch all on demand, this seems easiest if we've been here before bail out
|
|
PDOColumn *col = columns[0].toResource().getTyped<PDOColumn>();
|
|
if (!col->name.empty()) {
|
|
return true;
|
|
}
|
|
for (int i = 0; i < column_count; i++) {
|
|
col = columns[i].toResource().getTyped<PDOColumn>();
|
|
|
|
if (m_conn->fetch_table_names()) {
|
|
col->name = String(m_fields[i].table) + "." +
|
|
String(m_fields[i].name);
|
|
} else {
|
|
col->name = String(m_fields[i].name, CopyString);
|
|
}
|
|
|
|
col->precision = m_fields[i].decimals;
|
|
col->maxlen = m_fields[i].length;
|
|
col->param_type = PDO_PARAM_STR;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PDOMySqlStatement::getColumn(int colno, Variant &value) {
|
|
if (!m_result) {
|
|
return false;
|
|
}
|
|
|
|
if (!m_stmt) {
|
|
if (m_current_data == NULL || !m_result) {
|
|
return false;
|
|
}
|
|
}
|
|
if (colno < 0 || colno >= column_count) {
|
|
/* error invalid column */
|
|
return false;
|
|
}
|
|
char *ptr; int len;
|
|
if (m_stmt) {
|
|
if (m_out_null[colno]) {
|
|
value = String();
|
|
return true;
|
|
}
|
|
ptr = (char*)m_bound_result[colno].buffer;
|
|
if (m_out_length[colno] > m_bound_result[colno].buffer_length) {
|
|
/* mysql lied about the column width */
|
|
strcpy(error_code, "01004"); /* truncated */
|
|
m_out_length[colno] = m_bound_result[colno].buffer_length;
|
|
len = m_out_length[colno];
|
|
value = String(ptr, len, CopyString);
|
|
return false;
|
|
}
|
|
len = m_out_length[colno];
|
|
value = String(ptr, len, CopyString);
|
|
return true;
|
|
}
|
|
ptr = m_current_data[colno];
|
|
len = m_current_lengths[colno];
|
|
value = String(ptr, len, CopyString);
|
|
return true;
|
|
}
|
|
|
|
bool PDOMySqlStatement::paramHook(PDOBoundParam *param,
|
|
PDOParamEvent event_type) {
|
|
MYSQL_BIND *b;
|
|
if (m_stmt && param->is_param) {
|
|
switch (event_type) {
|
|
case PDO_PARAM_EVT_ALLOC:
|
|
/* sanity check parameter number range */
|
|
if (param->paramno < 0 || param->paramno >= m_num_params) {
|
|
strcpy(error_code, "HY093");
|
|
return false;
|
|
}
|
|
m_params_given++;
|
|
|
|
b = &m_params[param->paramno];
|
|
param->driver_data = b;
|
|
b->is_null = &m_in_null[param->paramno];
|
|
b->length = &m_in_length[param->paramno];
|
|
/* recall how many parameters have been provided */
|
|
return true;
|
|
|
|
case PDO_PARAM_EVT_EXEC_PRE:
|
|
if ((int)m_params_given < m_num_params) {
|
|
/* too few parameter bound */
|
|
strcpy(error_code, "HY093");
|
|
return false;
|
|
}
|
|
|
|
b = (MYSQL_BIND*)param->driver_data;
|
|
*b->is_null = 0;
|
|
if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL ||
|
|
param->parameter.isNull()) {
|
|
*b->is_null = 1;
|
|
b->buffer_type = MYSQL_TYPE_STRING;
|
|
b->buffer = NULL;
|
|
b->buffer_length = 0;
|
|
*b->length = 0;
|
|
return true;
|
|
}
|
|
|
|
switch (PDO_PARAM_TYPE(param->param_type)) {
|
|
case PDO_PARAM_STMT:
|
|
return false;
|
|
case PDO_PARAM_LOB:
|
|
if (param->parameter.isResource()) {
|
|
Variant buf = f_stream_get_contents(param->parameter.toResource());
|
|
if (!same(buf, false)) {
|
|
param->parameter = buf;
|
|
} else {
|
|
pdo_raise_impl_error(dbh, this, "HY105",
|
|
"Expected a stream resource");
|
|
return false;
|
|
}
|
|
}
|
|
/* fall through */
|
|
|
|
default:
|
|
;
|
|
}
|
|
|
|
if (param->parameter.isString()) {
|
|
String sparam = param->parameter.toString();
|
|
b->buffer_type = MYSQL_TYPE_STRING;
|
|
b->buffer = (void*)sparam.data();
|
|
b->buffer_length = sparam.size();
|
|
*b->length = sparam.size();
|
|
return true;
|
|
}
|
|
if (param->parameter.isInteger()) {
|
|
param->parameter = param->parameter.toInt64();
|
|
b->buffer_type = MYSQL_TYPE_LONG;
|
|
b->buffer = param->parameter.getInt64Data();
|
|
return true;
|
|
}
|
|
if (param->parameter.isDouble()) {
|
|
b->buffer_type = MYSQL_TYPE_DOUBLE;
|
|
b->buffer = param->parameter.getDoubleData();
|
|
return true;
|
|
}
|
|
return false;
|
|
case PDO_PARAM_EVT_FREE:
|
|
case PDO_PARAM_EVT_EXEC_POST:
|
|
case PDO_PARAM_EVT_FETCH_PRE:
|
|
case PDO_PARAM_EVT_FETCH_POST:
|
|
case PDO_PARAM_EVT_NORMALIZE:
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static const StaticString s_mysql_def("mysql:def");
|
|
static const StaticString s_not_null("not_null");
|
|
static const StaticString s_primary_key("primary_key");
|
|
static const StaticString s_multiple_key("multiple_key");
|
|
static const StaticString s_unique_key("unique_key");
|
|
static const StaticString s_blob("blob");
|
|
static const StaticString s_native_type("native_type");
|
|
static const StaticString s_flags("flags");
|
|
static const StaticString s_table("table");
|
|
|
|
bool PDOMySqlStatement::getColumnMeta(int64_t colno, Array &ret) {
|
|
if (!m_result) {
|
|
return false;
|
|
}
|
|
if (colno < 0 || colno >= column_count) {
|
|
/* error invalid column */
|
|
return false;
|
|
}
|
|
|
|
Array flags = Array::Create();
|
|
|
|
const MYSQL_FIELD *F = m_fields + colno;
|
|
if (F->def) {
|
|
ret.set(s_mysql_def, String(F->def, CopyString));
|
|
}
|
|
if (IS_NOT_NULL(F->flags)) {
|
|
flags.append(s_not_null);
|
|
}
|
|
if (IS_PRI_KEY(F->flags)) {
|
|
flags.append(s_primary_key);
|
|
}
|
|
if (F->flags & MULTIPLE_KEY_FLAG) {
|
|
flags.append(s_multiple_key);
|
|
}
|
|
if (F->flags & UNIQUE_KEY_FLAG) {
|
|
flags.append(s_unique_key);
|
|
}
|
|
if (IS_BLOB(F->flags)) {
|
|
flags.append(s_blob);
|
|
}
|
|
const char *str = type_to_name_native(F->type);
|
|
if (str) {
|
|
ret.set(s_native_type, str);
|
|
}
|
|
ret.set(s_flags, flags);
|
|
ret.set(s_table, String(F->table, CopyString));
|
|
return true;
|
|
}
|
|
|
|
bool PDOMySqlStatement::nextRowset() {
|
|
/* ensure that we free any previous unfetched results */
|
|
if (m_stmt) {
|
|
column_count = (int)mysql_num_fields(m_result);
|
|
mysql_stmt_free_result(m_stmt);
|
|
}
|
|
if (m_result) {
|
|
mysql_free_result(m_result);
|
|
m_result = NULL;
|
|
}
|
|
|
|
int ret = mysql_next_result(m_server);
|
|
if (ret > 0) {
|
|
handleError(__FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
if (ret < 0) {
|
|
/* No more results */
|
|
return false;
|
|
}
|
|
|
|
my_ulonglong row_count;
|
|
if (!m_conn->buffered()) {
|
|
m_result = mysql_use_result(m_server);
|
|
row_count = 0;
|
|
} else {
|
|
m_result = mysql_store_result(m_server);
|
|
if ((my_ulonglong)-1 == (row_count = mysql_affected_rows(m_server))) {
|
|
handleError(__FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!m_result) {
|
|
return false;
|
|
}
|
|
|
|
column_count = (int)mysql_num_fields(m_result);
|
|
m_fields = mysql_fetch_fields(m_result);
|
|
return true;
|
|
}
|
|
|
|
bool PDOMySqlStatement::cursorCloser() {
|
|
if (m_result) {
|
|
mysql_free_result(m_result);
|
|
m_result = NULL;
|
|
}
|
|
if (m_stmt) {
|
|
return !mysql_stmt_free_result(m_stmt);
|
|
}
|
|
|
|
while (mysql_more_results(m_server)) {
|
|
if (mysql_next_result(m_server) != 0) {
|
|
break;
|
|
}
|
|
MYSQL_RES *res = mysql_store_result(m_server);
|
|
if (res) {
|
|
mysql_free_result(res);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
PDOMySql::PDOMySql() : PDODriver("mysql") {
|
|
}
|
|
|
|
PDOConnection *PDOMySql::createConnectionObject() {
|
|
return new PDOMySqlConnection();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|