From: Andrey Hristov Date: Thu, 7 Feb 2013 17:45:49 +0000 (+0100) Subject: Add support for begin_transaction in libmysql mode. X-Git-Tag: php-5.5.0alpha5~31 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=afacdecd1f8001b143e86676859d570ebf4659b9;p=php Add support for begin_transaction in libmysql mode. Add support for flags and name for commit/rollback in libmysql mode --- diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 70a8cc6303..760ee3c424 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -30,6 +30,7 @@ #include "php_ini.h" #include "php_globals.h" #include "ext/standard/info.h" +#include "ext/standard/php_smart_str.h" #include "php_mysqli_structs.h" #include "mysqli_priv.h" @@ -635,18 +636,86 @@ PHP_FUNCTION(mysqli_close) } /* }}} */ + +#if !defined(MYSQLI_USE_MYSQLND) +/* {{{ mysqli_tx_cor_options_to_string */ +static void mysqli_tx_cor_options_to_string(const MYSQL * const conn, smart_str * str, const unsigned int mode) +{ + if (mode & TRANS_COR_AND_CHAIN && !(mode & TRANS_COR_AND_NO_CHAIN)) { + if (str->len) { + smart_str_appendl(str, ", ", sizeof(", ") - 1); + } + smart_str_appendl(str, "AND CHAIN", sizeof("AND CHAIN") - 1); + } else if (mode & TRANS_COR_AND_NO_CHAIN && !(mode & TRANS_COR_AND_CHAIN)) { + if (str->len) { + smart_str_appendl(str, ", ", sizeof(", ") - 1); + } + smart_str_appendl(str, "AND NO CHAIN", sizeof("AND NO CHAIN") - 1); + } + + if (mode & TRANS_COR_RELEASE && !(mode & TRANS_COR_NO_RELEASE)) { + if (str->len) { + smart_str_appendl(str, ", ", sizeof(", ") - 1); + } + smart_str_appendl(str, "RELEASE", sizeof("RELEASE") - 1); + } else if (mode & TRANS_COR_NO_RELEASE && !(mode & TRANS_COR_RELEASE)) { + if (str->len) { + smart_str_appendl(str, ", ", sizeof(", ") - 1); + } + smart_str_appendl(str, "NO RELEASE", sizeof("NO RELEASE") - 1); + } + smart_str_0(str); +} +/* }}} */ + + +/* {{{ proto bool mysqli_commit_or_rollback_libmysql */ +static int mysqli_commit_or_rollback_libmysql(MYSQL * conn, zend_bool commit, const unsigned int mode, const char * const name) +{ + int ret; + smart_str tmp_str = {0, 0, 0}; + mysqli_tx_cor_options_to_string(conn, &tmp_str, mode); + smart_str_0(&tmp_str); + + { + char * commented_name = NULL; + unsigned int commented_name_len = name? spprintf(&commented_name, 0, " /*%s*/", name):0; + char * query; + unsigned int query_len = spprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"), + commented_name? commented_name:"", tmp_str.c? tmp_str.c:""); + smart_str_free(&tmp_str); + + ret = mysql_real_query(conn, query, query_len); + efree(query); + if (commented_name) { + efree(commented_name); + } + } +} +/* }}} */ +#endif + + /* {{{ proto bool mysqli_commit(object link) Commit outstanding actions and close transaction */ PHP_FUNCTION(mysqli_commit) { MY_MYSQL *mysql; zval *mysql_link; + long flags = TRANS_COR_NO_OPT; + char * name = NULL; + int name_len = 0; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|ls", &mysql_link, mysqli_link_class_entry, &flags, &name, &name_len) == FAILURE) { return; } MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_VALID); - if (mysql_commit(mysql->mysql)) { + +#if !defined(MYSQLI_USE_MYSQLND) + if (mysqli_commit_or_rollback_libmysql(mysql->mysql, TRUE, flags, name)) { +#else + if (mysqlnd_commit(mysql->mysql, flags, name)) { +#endif RETURN_FALSE; } RETURN_TRUE; @@ -1872,19 +1941,27 @@ PHP_FUNCTION(mysqli_real_escape_string) { } /* }}} */ + /* {{{ proto bool mysqli_rollback(object link) Undo actions from current transaction */ PHP_FUNCTION(mysqli_rollback) { MY_MYSQL *mysql; zval *mysql_link; + long flags = TRANS_COR_NO_OPT; + char * name = NULL; + int name_len = 0; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|ls", &mysql_link, mysqli_link_class_entry, &flags, &name, &name_len) == FAILURE) { return; } MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_VALID); - if (mysql_rollback(mysql->mysql)) { +#if !defined(MYSQLI_USE_MYSQLND) + if (mysqli_commit_or_rollback_libmysql(mysql->mysql, FALSE, flags, name)) { +#else + if (mysqlnd_rollback(mysql->mysql, flags, name)) { +#endif RETURN_FALSE; } RETURN_TRUE; diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c index 4ac67269ce..411ab11979 100644 --- a/ext/mysqli/mysqli_fe.c +++ b/ext/mysqli/mysqli_fe.c @@ -86,6 +86,38 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_autocommit, 0, 0, 1) ZEND_ARG_INFO(0, mode) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_begin_transaction, 0, 0, 1) + MYSQLI_ZEND_ARG_OBJ_INFO_LINK() + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_begin_transaction, 0, 0, 0) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_commit, 0, 0, 1) + MYSQLI_ZEND_ARG_OBJ_INFO_LINK() + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_commit, 0, 0, 0) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_rollback, 0, 0, 1) + MYSQLI_ZEND_ARG_OBJ_INFO_LINK() + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_rollback, 0, 0, 0) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_change_user, 0, 0, 4) MYSQLI_ZEND_ARG_OBJ_INFO_LINK() @@ -326,10 +358,11 @@ ZEND_END_ARG_INFO() const zend_function_entry mysqli_functions[] = { PHP_FE(mysqli_affected_rows, arginfo_mysqli_only_link) PHP_FE(mysqli_autocommit, arginfo_mysqli_autocommit) + PHP_FE(mysqli_begin_transaction, arginfo_mysqli_begin_transaction) PHP_FE(mysqli_change_user, arginfo_mysqli_change_user) PHP_FE(mysqli_character_set_name, arginfo_mysqli_only_link) PHP_FE(mysqli_close, arginfo_mysqli_only_link) - PHP_FE(mysqli_commit, arginfo_mysqli_only_link) + PHP_FE(mysqli_commit, arginfo_mysqli_commit) PHP_FE(mysqli_connect, arginfo_mysqli_connect) PHP_FE(mysqli_connect_errno, arginfo_mysqli_no_params) PHP_FE(mysqli_connect_error, arginfo_mysqli_no_params) @@ -397,7 +430,7 @@ const zend_function_entry mysqli_functions[] = { #if defined(MYSQLI_USE_MYSQLND) PHP_FE(mysqli_reap_async_query, arginfo_mysqli_only_link) #endif - PHP_FE(mysqli_rollback, arginfo_mysqli_only_link) + PHP_FE(mysqli_rollback, arginfo_mysqli_rollback) PHP_FE(mysqli_select_db, arginfo_mysqli_select_db) #ifdef HAVE_MYSQLI_SET_CHARSET PHP_FE(mysqli_set_charset, arginfo_mysqli_set_charset) @@ -458,10 +491,11 @@ const zend_function_entry mysqli_functions[] = { */ const zend_function_entry mysqli_link_methods[] = { PHP_FALIAS(autocommit, mysqli_autocommit, arginfo_class_mysqli_autocommit) + PHP_FALIAS(begin_transaction, mysqli_begin_transaction, arginfo_class_mysqli_begin_transaction) PHP_FALIAS(change_user,mysqli_change_user, arginfo_class_mysqli_change_user) PHP_FALIAS(character_set_name, mysqli_character_set_name, arginfo_mysqli_no_params) PHP_FALIAS(close, mysqli_close, arginfo_mysqli_no_params) - PHP_FALIAS(commit, mysqli_commit, arginfo_mysqli_no_params) + PHP_FALIAS(commit, mysqli_commit, arginfo_class_mysqli_commit) PHP_FALIAS(connect, mysqli_connect, arginfo_mysqli_connect) PHP_FALIAS(dump_debug_info, mysqli_dump_debug_info, arginfo_mysqli_no_params) PHP_FALIAS(debug, mysqli_debug, arginfo_mysqli_debug) @@ -494,7 +528,7 @@ const zend_function_entry mysqli_link_methods[] = { #endif PHP_FALIAS(escape_string, mysqli_real_escape_string, arginfo_class_mysqli_real_escape_string) PHP_FALIAS(real_query, mysqli_real_query, arginfo_class_mysqli_query) - PHP_FALIAS(rollback,mysqli_rollback, arginfo_mysqli_no_params) + PHP_FALIAS(rollback, mysqli_rollback, arginfo_class_mysqli_rollback) PHP_FALIAS(select_db,mysqli_select_db, arginfo_class_mysqli_select_db) #ifdef HAVE_MYSQLI_SET_CHARSET PHP_FALIAS(set_charset, mysqli_set_charset, arginfo_class_mysqli_set_charset) diff --git a/ext/mysqli/mysqli_fe.h b/ext/mysqli/mysqli_fe.h index 2edb32cb31..e6cd3a6678 100644 --- a/ext/mysqli/mysqli_fe.h +++ b/ext/mysqli/mysqli_fe.h @@ -25,6 +25,7 @@ PHP_FUNCTION(mysqli); PHP_FUNCTION(mysqli_affected_rows); PHP_FUNCTION(mysqli_autocommit); +PHP_FUNCTION(mysqli_begin_transaction); PHP_FUNCTION(mysqli_change_user); PHP_FUNCTION(mysqli_character_set_name); PHP_FUNCTION(mysqli_set_charset); diff --git a/ext/mysqli/mysqli_libmysql.h b/ext/mysqli/mysqli_libmysql.h index 3a7b91b995..e10e3702ea 100644 --- a/ext/mysqli/mysqli_libmysql.h +++ b/ext/mysqli/mysqli_libmysql.h @@ -42,6 +42,18 @@ #define mysqli_change_user_silent(c, u, p, d, p_len) mysql_change_user((c), (u), (p), (d)) +#define TRANS_START_NO_OPT 0 +#define TRANS_START_WITH_CONSISTENT_SNAPSHOT 1 +#define TRANS_START_READ_WRITE 2 +#define TRANS_START_READ_ONLY 4 + +#define TRANS_COR_NO_OPT 0 +#define TRANS_COR_AND_CHAIN 1 +#define TRANS_COR_AND_NO_CHAIN 2 +#define TRANS_COR_RELEASE 4 +#define TRANS_COR_NO_RELEASE 8 + + /* These functions also reside in ext/mysqlnd/mysqlnd_portability.h but since it is only made available if one wants to build mysqli against mysqlnd and they are useful for libmysql as diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index b730d24cd6..c08fbaef28 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -29,6 +29,7 @@ #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "ext/standard/php_smart_str.h" #include "php_mysqli_structs.h" #include "mysqli_priv.h" @@ -1045,6 +1046,81 @@ PHP_FUNCTION(mysqli_get_charset) /* }}} */ #endif + +#if !defined(MYSQLI_USE_MYSQLND) +/* {{{ proto bool mysqli_begin_transaction_libmysql */ +static int mysqli_begin_transaction_libmysql(MYSQL * conn, const unsigned int mode, const char * const name) +{ + int ret; + smart_str tmp_str = {0, 0, 0}; + if (mode & TRANS_START_WITH_CONSISTENT_SNAPSHOT) { + if (tmp_str.len) { + smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1); + } + smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1); + } + if (mode & TRANS_START_READ_WRITE) { + if (tmp_str.len) { + smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1); + } + smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1); + } + if (mode & TRANS_START_READ_ONLY) { + if (tmp_str.len) { + smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1); + } + smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1); + } + smart_str_0(&tmp_str); + + { + char * commented_name = NULL; + unsigned int commented_name_len = name? spprintf(&commented_name, 0, " /*%s*/", name):0; + char * query; + unsigned int query_len = spprintf(&query, 0, "START TRANSACTION%s %s", + commented_name? commented_name:"", tmp_str.c? tmp_str.c:""); + smart_str_free(&tmp_str); + + ret = mysql_real_query(conn, query, query_len); + efree(query); + if (commented_name) { + efree(commented_name); + } + } + return ret; +} +/* }}} */ +#endif + +/* {{{ proto bool mysqli_begin_transaction(object link, [int flags [, string name]]) + Starts a transaction */ +PHP_FUNCTION(mysqli_begin_transaction) +{ + MY_MYSQL *mysql; + zval *mysql_link; + long flags = TRANS_START_NO_OPT; + char * name = NULL; + int name_len = 0; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|ls", &mysql_link, mysqli_link_class_entry, &flags, &name, &name_len) == FAILURE) { + return; + } + MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_VALID); + +#if !defined(MYSQLI_USE_MYSQLND) + if (mysqli_begin_transaction_libmysql(mysql->mysql, flags, name)) { + RETURN_FALSE; + } +#else + if (mysqlnd_begin_transaction(mysql->mysql, flags, name)) { + RETURN_FALSE; + } +#endif + RETURN_TRUE; +} +/* }}} */ + + /* * Local variables: * tab-width: 4 diff --git a/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt b/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt index 19ba0086fb..44e9865e17 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt @@ -20,38 +20,39 @@ require_once('skipifconnectfailure.inc'); $methods = get_class_methods($mysqli); $expected_methods = array( 'autocommit' => true, + 'begin_transaction' => true, 'change_user' => true, - 'character_set_name' => true, - 'close' => true, - 'commit' => true, - 'connect' => true, + 'character_set_name' => true, + 'close' => true, + 'commit' => true, + 'connect' => true, 'dump_debug_info' => true, 'escape_string' => true, 'get_charset' => true, 'get_client_info' => true, 'get_server_info' => true, 'get_warnings' => true, - 'init' => true, - 'kill' => true, + 'init' => true, + 'kill' => true, 'more_results' => true, 'multi_query' => true, - 'mysqli' => true, + 'mysqli' => true, 'next_result' => true, - 'options' => true, - 'ping' => true, - 'prepare' => true, - 'query' => true, + 'options' => true, + 'ping' => true, + 'prepare' => true, + 'query' => true, 'real_connect' => true, - 'real_escape_string' => true, + 'real_escape_string' => true, 'real_query' => true, - 'refresh' => true, - 'rollback' => true, - 'select_db' => true, + 'refresh' => true, + 'rollback' => true, + 'select_db' => true, 'set_charset' => true, - 'set_opt' => true, - 'ssl_set' => true, - 'stat' => true, - 'stmt_init' => true, + 'set_opt' => true, + 'ssl_set' => true, + 'stat' => true, + 'stmt_init' => true, 'store_result' => true, 'thread_safe' => true, 'use_result' => true, diff --git a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt index 5fd4b6f45c..c62cb52fc7 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt @@ -6,8 +6,6 @@ require_once('skipif.inc'); require_once('skipifemb.inc'); require_once('connect.inc'); -if (($tmp = substr(PHP_VERSION, 0, strpos(PHP_VERSION, '.'))) && ($tmp < 5)) - die("skip Reflection not available before PHP 5 (found PHP $tmp)"); /* Let's not deal with cross-version issues in the EXPECTF/UEXPECTF. Most of the things which we test are covered by mysqli_class_*_interface.phpt. @@ -120,6 +118,36 @@ isPassedByReference: no isOptional: no isDefaultValueAvailable: no +Inspecting method 'begin_transaction' +isFinal: no +isAbstract: no +isPublic: yes +isPrivate: no +isProtected: no +isStatic: no +isConstructor: no +isDestructor: no +isInternal: yes +isUserDefined: no +returnsReference: no +Modifiers: 256 +Number of Parameters: 2 +Number of Required Parameters: 0 + +Inspecting parameter 'flags' of method 'begin_transaction' +isArray: no +allowsNull: no +isPassedByReference: no +isOptional: yes +isDefaultValueAvailable: no + +Inspecting parameter 'name' of method 'begin_transaction' +isArray: no +allowsNull: no +isPassedByReference: no +isOptional: yes +isDefaultValueAvailable: no + Inspecting method 'change_user' isFinal: no isAbstract: no @@ -202,9 +230,23 @@ isInternal: yes isUserDefined: no returnsReference: no Modifiers: 256 -Number of Parameters: 0 +Number of Parameters: 2 Number of Required Parameters: 0 +Inspecting parameter 'flags' of method 'commit' +isArray: no +allowsNull: no +isPassedByReference: no +isOptional: yes +isDefaultValueAvailable: no + +Inspecting parameter 'name' of method 'commit' +isArray: no +allowsNull: no +isPassedByReference: no +isOptional: yes +isDefaultValueAvailable: no + Inspecting method 'connect' isFinal: no isAbstract: no @@ -863,9 +905,23 @@ isInternal: yes isUserDefined: no returnsReference: no Modifiers: 256 -Number of Parameters: 0 +Number of Parameters: 2 Number of Required Parameters: 0 +Inspecting parameter 'flags' of method 'rollback' +isArray: no +allowsNull: no +isPassedByReference: no +isOptional: yes +isDefaultValueAvailable: no + +Inspecting parameter 'name' of method 'rollback' +isArray: no +allowsNull: no +isPassedByReference: no +isOptional: yes +isDefaultValueAvailable: no + Inspecting method 'select_db' isFinal: no isAbstract: no diff --git a/ext/mysqli/tests/mysqli_commit_oo.phpt b/ext/mysqli/tests/mysqli_commit_oo.phpt index 34ec4bfcdc..e19f698e81 100644 --- a/ext/mysqli/tests/mysqli_commit_oo.phpt +++ b/ext/mysqli/tests/mysqli_commit_oo.phpt @@ -28,12 +28,8 @@ if (!have_innodb($link)) printf("[001] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", $host, $user, $db, $port, $socket); - if (!is_null($tmp = @$mysqli->commit($link))) - printf("[002] Expecting NULL/NULL, got %s/%s, [%d] %s\n", - gettype($tmp), $tmp, $mysqli->errno, $mysqli->error); - if (true !== ($tmp = $mysqli->commit())) - printf("[014] Expecting boolean/true got %s/%s\n", gettype($tmp), $tmp); + printf("[002] Expecting boolean/true got %s/%s\n", gettype($tmp), $tmp); if (true !== ($tmp = $mysqli->autocommit(false))) printf("[003] Cannot turn off autocommit, expecting true, got %s/%s\n", gettype($tmp), $tmp);