diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php index 342bebcc9..36fe76e25 100644 --- a/classes/context/Context.class.php +++ b/classes/context/Context.class.php @@ -401,13 +401,6 @@ class Context function close() { session_write_close(); - - // DB close - $oDB = DB::getInstance(); - if(is_object($oDB) && method_exists($oDB, 'close')) - { - $oDB->close(); - } } /** diff --git a/classes/db/DB.class.php b/classes/db/DB.class.php index aca61aa7f..f2167a03b 100644 --- a/classes/db/DB.class.php +++ b/classes/db/DB.class.php @@ -57,7 +57,8 @@ class DB * @var array */ var $priority_dbms = array( - 'mysqli' => 5, + 'mysqli' => 6, + 'mysqli_innodb' => 5, 'mysql' => 4, 'mysql_innodb' => 3, 'cubrid' => 2, @@ -468,7 +469,8 @@ class DB { $log['result'] = 'Success'; } - $GLOBALS['__db_queries__'][] = $log; + + $this->setQueryLog($log); // if __LOG_SLOW_QUERY__ if defined, check elapsed time and leave query log if(__LOG_SLOW_QUERY__ > 0 && $elapsed_time > __LOG_SLOW_QUERY__) @@ -486,6 +488,16 @@ class DB } } + /** + * set query debug log + * @param array $log values set query debug + * @return void + */ + function setQueryLog($log) + { + $GLOBALS['__db_queries__'][] = $log; + } + /** * set error * @param int $errno error code @@ -1089,6 +1101,7 @@ class DB $connection = &$this->slave_db[$indx]; } + $this->commit(); $this->_close($connection["resource"]); $connection["is_connected"] = FALSE; @@ -1313,6 +1326,9 @@ class DB // Save connection info for db logs $this->connection = ucfirst($type) . ' ' . $connection["db_hostname"]; + // regist $this->close callback + register_shutdown_function(array($this, "close")); + $this->_afterConnect($result); } diff --git a/classes/db/DBMysql_innodb.class.php b/classes/db/DBMysql_innodb.class.php index 5a03950f5..affa59aab 100644 --- a/classes/db/DBMysql_innodb.class.php +++ b/classes/db/DBMysql_innodb.class.php @@ -43,7 +43,6 @@ class DBMysql_innodb extends DBMysql */ function _close($connection) { - $this->_query("commit", $connection); @mysql_close($connection); } diff --git a/classes/db/DBMysqli_innodb.class.php b/classes/db/DBMysqli_innodb.class.php new file mode 100644 index 000000000..9f5ee5671 --- /dev/null +++ b/classes/db/DBMysqli_innodb.class.php @@ -0,0 +1,583 @@ + */ + +require_once('DBMysql.class.php'); + +/** + * Class to use MySQLi innoDB DBMS as mysqli_* + * mysql innodb handling class + * + * Does not use prepared statements, since mysql driver does not support them + * + * @author NAVER (developers@xpressengine.com) + * @package /classes/db + * @version 0.1 + */ +class DBMysqli_innodb extends DBMysql +{ + + /** + * Constructor + * @return void + */ + function DBMysqli_innodb() + { + $this->_setDBInfo(); + $this->_connect(); + } + + /** + * Create an instance of this class + * @return DBMysqli_innodb return DBMysqli_innodb object instance + */ + function create() + { + return new DBMysqli_innodb; + } + + /** + * DB Connect + * this method is private + * @param array $connection connection's value is db_hostname, db_port, db_database, db_userid, db_password + * @return resource + */ + function __connect($connection) + { + // Attempt to connect + if($connection["db_port"]) + { + $result = @mysqli_connect($connection["db_hostname"] + , $connection["db_userid"] + , $connection["db_password"] + , $connection["db_database"] + , $connection["db_port"]); + } + else + { + $result = @mysqli_connect($connection["db_hostname"] + , $connection["db_userid"] + , $connection["db_password"] + , $connection["db_database"]); + } + $error = mysqli_connect_errno(); + if($error) + { + $this->setError($error, mysqli_connect_error()); + return; + } + mysqli_set_charset($result, 'utf8'); + return $result; + } + + /** + * DB disconnection + * this method is private + * @param resource $connection + * @return void + */ + function _close($connection) + { + mysqli_close($connection); + } + + /** + * DB transaction start + * this method is private + * @return boolean + */ + function _begin($transactionLevel) + { + $connection = $this->_getConnection('master'); + + if(!$transactionLevel) + { + $this->_query("begin"); + } + else + { + $this->_query("SAVEPOINT SP" . $transactionLevel, $connection); + } + return true; + } + + /** + * DB transaction rollback + * this method is private + * @return boolean + */ + function _rollback($transactionLevel) + { + $connection = $this->_getConnection('master'); + + $point = $transactionLevel - 1; + + if($point) + { + $this->_query("ROLLBACK TO SP" . $point, $connection); + } + else + { + mysqli_rollback($connection); + $this->setQueryLog( array("query"=>"rollback") ); + } + return true; + } + + /** + * DB transaction commit + * this method is private + * @return boolean + */ + function _commit() + { + $connection = $this->_getConnection('master'); + mysqli_commit($connection); + $this->setQueryLog( array("query"=>"commit") ); + return true; + } + + + + /** + * Handles quatation of the string variables from the query + * @param string $string + * @return string + */ + function addQuotes($string) + { + if(version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) + { + $string = stripslashes(str_replace("\\", "\\\\", $string)); + } + if(!is_numeric($string)) + { + $connection = $this->_getConnection('master'); + $string = mysqli_escape_string($connection, $string); + } + return $string; + } + + /** + * Execute the query + * this method is private + * @param string $query + * @param resource $connection + * @return resource + */ + function __query($query, $connection) + { + if($this->use_prepared_statements == 'Y') + { + // 1. Prepare query + $stmt = mysqli_prepare($connection, $query); + if($stmt) + { + $types = ''; + $params = array(); + $this->_prepareQueryParameters($types, $params); + + if(!empty($params)) + { + $args[0] = $stmt; + $args[1] = $types; + + $i = 2; + foreach($params as $key => $param) + { + $copy[$key] = $param; + $args[$i++] = &$copy[$key]; + } + + // 2. Bind parameters + $status = call_user_func_array('mysqli_stmt_bind_param', $args); + if(!$status) + { + $this->setError(-1, "Invalid arguments: $query" . mysqli_error($connection) . PHP_EOL . print_r($args, true)); + } + } + + // 3. Execute query + $status = mysqli_stmt_execute($stmt); + + if(!$status) + { + $this->setError(-1, "Prepared statement failed: $query" . mysqli_error($connection) . PHP_EOL . print_r($args, true)); + } + + // Return stmt for other processing - like retrieving resultset (_fetch) + return $stmt; + // mysqli_stmt_close($stmt); + } + } + // Run the query statement + $result = mysqli_query($connection, $query); + // Error Check + $error = mysqli_error($connection); + if($error) + { + $this->setError(mysqli_errno($connection), $error); + } + // Return result + return $result; + } + + /** + * Before execute query, prepare statement + * this method is private + * @param string $types + * @param array $params + * @return void + */ + function _prepareQueryParameters(&$types, &$params) + { + $types = ''; + $params = array(); + if(!$this->param) + { + return; + } + + foreach($this->param as $k => $o) + { + $value = $o->getUnescapedValue(); + $type = $o->getType(); + + // Skip column names -> this should be concatenated to query string + if($o->isColumnName()) + { + continue; + } + + switch($type) + { + case 'number' : + $type = 'i'; + break; + case 'varchar' : + $type = 's'; + break; + default: + $type = 's'; + } + + if(is_array($value)) + { + foreach($value as $v) + { + $params[] = $v; + $types .= $type; + } + } + else + { + $params[] = $value; + $types .= $type; + } + } + } + + /** + * Fetch the result + * @param resource $result + * @param int|NULL $arrayIndexEndValue + * @return array + */ + function _fetch($result, $arrayIndexEndValue = NULL) + { + if($this->use_prepared_statements != 'Y') + { + return parent::_fetch($result, $arrayIndexEndValue); + } + $output = array(); + if(!$this->isConnected() || $this->isError() || !$result) + { + return $output; + } + + // Prepared stements: bind result variable and fetch data + $stmt = $result; + $meta = mysqli_stmt_result_metadata($stmt); + $fields = mysqli_fetch_fields($meta); + + /** + * Mysqli has a bug that causes LONGTEXT columns not to get loaded + * Unless store_result is called before + * MYSQLI_TYPE for longtext is 252 + */ + $longtext_exists = false; + foreach($fields as $field) + { + if(isset($resultArray[$field->name])) // When joined tables are used and the same column name appears twice, we should add it separately, otherwise bind_result fails + { + $field->name = 'repeat_' . $field->name; + } + + // Array passed needs to contain references, not values + $row[$field->name] = ""; + $resultArray[$field->name] = &$row[$field->name]; + + if($field->type == 252) + { + $longtext_exists = true; + } + } + $resultArray = array_merge(array($stmt), $resultArray); + + if($longtext_exists) + { + mysqli_stmt_store_result($stmt); + } + + call_user_func_array('mysqli_stmt_bind_result', $resultArray); + + $rows = array(); + while(mysqli_stmt_fetch($stmt)) + { + $resultObject = new stdClass(); + + foreach($resultArray as $key => $value) + { + if($key === 0) + { + continue; // Skip stmt object + } + if(strpos($key, 'repeat_')) + { + $key = substr($key, 6); + } + $resultObject->$key = $value; + } + + $rows[] = $resultObject; + } + + mysqli_stmt_close($stmt); + + if($arrayIndexEndValue) + { + foreach($rows as $row) + { + $output[$arrayIndexEndValue--] = $row; + } + } + else + { + $output = $rows; + } + + if(count($output) == 1) + { + if(isset($arrayIndexEndValue)) + { + return $output; + } + else + { + return $output[0]; + } + } + + return $output; + } + + /** + * Handles insertAct + * @param Object $queryObject + * @param boolean $with_values + * @return resource + */ + function _executeInsertAct($queryObject, $with_values = false) + { + if($this->use_prepared_statements != 'Y') + { + return parent::_executeInsertAct($queryObject); + } + $this->param = $queryObject->getArguments(); + $result = parent::_executeInsertAct($queryObject, $with_values); + unset($this->param); + return $result; + } + + /** + * Handles updateAct + * @param Object $queryObject + * @param boolean $with_values + * @return resource + */ + function _executeUpdateAct($queryObject, $with_values = false) + { + if($this->use_prepared_statements != 'Y') + { + return parent::_executeUpdateAct($queryObject); + } + $this->param = $queryObject->getArguments(); + $result = parent::_executeUpdateAct($queryObject, $with_values); + unset($this->param); + return $result; + } + + /** + * Handles deleteAct + * @param Object $queryObject + * @param boolean $with_values + * @return resource + */ + function _executeDeleteAct($queryObject, $with_values = false) + { + if($this->use_prepared_statements != 'Y') + { + return parent::_executeDeleteAct($queryObject); + } + $this->param = $queryObject->getArguments(); + $result = parent::_executeDeleteAct($queryObject, $with_values); + unset($this->param); + return $result; + } + + /** + * Handle selectAct + * In order to get a list of pages easily when selecting \n + * it supports a method as navigation + * @param Object $queryObject + * @param resource $connection + * @param boolean $with_values + * @return Object + */ + function _executeSelectAct($queryObject, $connection = null, $with_values = false) + { + if($this->use_prepared_statements != 'Y') + { + return parent::_executeSelectAct($queryObject, $connection); + } + $this->param = $queryObject->getArguments(); + $result = parent::_executeSelectAct($queryObject, $connection, $with_values); + unset($this->param); + return $result; + } + + /** + * Get the ID generated in the last query + * Return next sequence from sequence table + * This method use only mysql + * @return int + */ + function db_insert_id() + { + $connection = $this->_getConnection('master'); + return mysqli_insert_id($connection); + } + + /** + * Fetch a result row as an object + * @param resource $result + * @return object + */ + function db_fetch_object(&$result) + { + return mysqli_fetch_object($result); + } + + /** + * Free result memory + * @param resource $result + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function db_free_result(&$result) + { + return mysqli_free_result($result); + } + + /** + * Create table by using the schema xml + * + * type : number, varchar, tinytext, text, bigtext, char, date, \n + * opt : notnull, default, size\n + * index : primary key, index, unique\n + * @param string $xml_doc xml schema contents + * @return void|object + */ + function _createTable($xml_doc) + { + // xml parsing + $oXml = new XmlParser(); + $xml_obj = $oXml->parse($xml_doc); + // Create a table schema + $table_name = $xml_obj->table->attrs->name; + if($this->isTableExists($table_name)) + { + return; + } + $table_name = $this->prefix . $table_name; + + if(!is_array($xml_obj->table->column)) + { + $columns[] = $xml_obj->table->column; + } + else + { + $columns = $xml_obj->table->column; + } + + foreach($columns as $column) + { + $name = $column->attrs->name; + $type = $column->attrs->type; + $size = $column->attrs->size; + $notnull = $column->attrs->notnull; + $primary_key = $column->attrs->primary_key; + $index = $column->attrs->index; + $unique = $column->attrs->unique; + $default = $column->attrs->default; + $auto_increment = $column->attrs->auto_increment; + + $column_schema[] = sprintf('`%s` %s%s %s %s %s', $name, $this->column_type[$type], $size ? '(' . $size . ')' : '', isset($default) ? "default '" . $default . "'" : '', $notnull ? 'not null' : '', $auto_increment ? 'auto_increment' : ''); + + if($primary_key) + { + $primary_list[] = $name; + } + else if($unique) + { + $unique_list[$unique][] = $name; + } + else if($index) + { + $index_list[$index][] = $name; + } + } + + if(count($primary_list)) + { + $column_schema[] = sprintf("primary key (%s)", '`' . implode($primary_list, '`,`') . '`'); + } + + if(count($unique_list)) + { + foreach($unique_list as $key => $val) + { + $column_schema[] = sprintf("unique %s (%s)", $key, '`' . implode($val, '`,`') . '`'); + } + } + + if(count($index_list)) + { + foreach($index_list as $key => $val) + { + $column_schema[] = sprintf("index %s (%s)", $key, '`' . implode($val, '`,`') . '`'); + } + } + + $schema = sprintf('create table `%s` (%s%s) %s;', $this->addQuotes($table_name), "\n", implode($column_schema, ",\n"), "ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_general_ci"); + + $output = $this->_query($schema); + if(!$output) + { + return false; + } + } +} + +DBMysqli_innodb::$isSupported = function_exists('mysqli_connect'); + +/* End of file DBMysqli.class.php */ +/* Location: ./classes/db/DBMysqli.class.php */ diff --git a/modules/install/lang/lang.xml b/modules/install/lang/lang.xml index a62f01978..0ac55bdf2 100644 --- a/modules/install/lang/lang.xml +++ b/modules/install/lang/lang.xml @@ -354,6 +354,19 @@ İşlemler, veritabanı dosyası myisam 'da oluşturulduğu zaman işlenmeyecektir.]]> mysql*() để sử dụng MySql Database.
Giao dịch được vô hiệu hóa bởi File Database được tạo ra bởi myisam.]]>
+ + innodb는 트랜잭션을 사용할 수 있습니다.]]> + Transactions will be processed with innodb]]> + 「innodb」ではトランザクションの処理が行えます。]]> + innodb可以使用transaction。]]> + innodb可以使用 transaction。]]> + La transaction sera validé pour innodb]]> + Транзакция включена для innodb]]> + La transacción es hablilitado para innodb]]> + İşlemler, innodb ile işlenecektir]]> + innodb để sử dụng MySql Database.
Giao dịch được kích hoạt cho innodb]]>
+
+ DB 파일은 myisam으로 생성되기에 트랜잭션이 이루어지지 않습니다.]]> Transactions will not be processed since DB file is created in myisam]]> diff --git a/modules/install/tpl/form.mysqli_innodb.html b/modules/install/tpl/form.mysqli_innodb.html new file mode 100644 index 000000000..358e962f5 --- /dev/null +++ b/modules/install/tpl/form.mysqli_innodb.html @@ -0,0 +1,54 @@ + + +
+ +
+
+

{$XE_VALIDATOR_MESSAGE}

+
+
+ + + +

{$title}

+
+ +
+
+
+ +
+
+
+ +
+
+
{$lang->db_info_desc}
+

{$lang->advanced_setup}

+
+
+ +
+
+
+ +
+
+
+ +
+
+
{$lang->db_prefix_desc}
+
+
+ + {$lang->cmd_back} + +
+ +
+
+
+
+
+