Skip to content

Commit

Permalink
Switches MySQLDatabase to use the MySQLi class rather than the mysql_…
Browse files Browse the repository at this point in the history
…* functions.
  • Loading branch information
simonwelsh committed Oct 29, 2011
1 parent 0cdf52c commit 6bf3f7d
Showing 1 changed file with 82 additions and 55 deletions.
137 changes: 82 additions & 55 deletions model/MySQLDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class MySQLDatabase extends SS_Database {

private static $connection_charset = null;

private $supportsTransactions=false;
private $supportsTransactions = true;

/**
* Sets the character set for the MySQL database connection.
Expand All @@ -54,22 +54,22 @@ public static function set_connection_charset($charset = 'utf8') {
* - timezone: (optional) The timezone offset. For example: +12:00, "Pacific/Auckland", or "SYSTEM"
*/
public function __construct($parameters) {
$this->dbConn = mysql_connect($parameters['server'], $parameters['username'], $parameters['password'], true);
$this->dbConn = new MySQLi($parameters['server'], $parameters['username'], $parameters['password']);

if($this->dbConn->connect_error) {
$this->databaseError("Couldn't connect to MySQL database | " . $this->dbConn->connect_error);
}

$this->query("SET sql_mode = 'ANSI'");

if(self::$connection_charset) {
$this->query("SET CHARACTER SET '" . self::$connection_charset . "'");
$this->query("SET NAMES '" . self::$connection_charset . "'");
$this->dbConn->set_charset(self::$connection_charset);
}

$this->active = mysql_select_db($parameters['database'], $this->dbConn);
$this->active = $this->dbConn->select_db($parameters['database']);
$this->database = $parameters['database'];

if(!$this->dbConn) {
$this->databaseError("Couldn't connect to MySQL database");
}

if(isset($parameters['timezone'])) $this->query(sprintf("SET SESSION time_zone = '%s'", $parameters['timezone']));
$this->query("SET sql_mode = 'ANSI'");
}

/**
Expand All @@ -84,15 +84,15 @@ public function getConnect($parameters) {
* @return boolean
*/
public function supportsCollations() {
return $this->getVersion() >= 4.1;
return true;
}

/**
* Get the version of MySQL.
* @return string
*/
public function getVersion() {
return mysql_get_server_info($this->dbConn);
return $this->dbConn->server_info;
}

/**
Expand All @@ -113,35 +113,33 @@ public function query($sql, $errorLevel = E_USER_ERROR) {
$starttime = microtime(true);
}

$handle = mysql_query($sql, $this->dbConn);
$handle = $this->dbConn->query($sql);

if(isset($_REQUEST['showqueries'])) {
$endtime = round(microtime(true) - $starttime,4);
Debug::message("\n$sql\n{$endtime}ms\n", false);
}

if(!$handle && $errorLevel) $this->databaseError("Couldn't run query: $sql | " . mysql_error($this->dbConn), $errorLevel);
if(!$handle && $errorLevel) $this->databaseError("Couldn't run query: $sql | " . $this->dbConn->error, $errorLevel);
return new MySQLQuery($this, $handle);
}

public function getGeneratedID($table) {
return mysql_insert_id($this->dbConn);
return $this->dbConn->insert_id;
}

public function isActive() {
return $this->active ? true : false;
}

public function createDatabase() {
$this->query("CREATE DATABASE `$this->database`");
$this->query("USE `$this->database`");
$this->query("CREATE DATABASE \"$this->database\"");
$this->query("USE \"$this->database\"");

$this->tableList = $this->fieldList = $this->indexList = null;

if(mysql_select_db($this->database, $this->dbConn)) {
$this->active = true;
return true;
}
$this->active = $this->dbConn->select_db($this->database);
return $this->active;
}

/**
Expand Down Expand Up @@ -173,10 +171,12 @@ public function currentDatabase() {
*/
public function selectDatabase($dbname) {
$this->database = $dbname;
$this->tableList = $this->fieldList = $this->indexList = null;
$this->active = false;
if($this->databaseExists($this->database)) {
if(mysql_select_db($this->database, $this->dbConn)) $this->active = true;
$this->active = $this->dbConn->select_db($this->database);
}
$this->tableList = $this->fieldList = $this->indexList = null;
return $this->active;
}

/**
Expand Down Expand Up @@ -206,9 +206,15 @@ public function allDatabaseNames() {
*/
public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null) {
$fieldSchemas = $indexSchemas = "";

$addOptions = empty($options[get_class($this)]) ? "ENGINE=MyISAM" : $options[get_class($this)];


if(!empty($options[get_class($this)])) {
$addOptions = $options[get_class($this)];
} elseif(!empty($options[get_parent_class($this)])) {
$addOptions = $options[get_parent_class($this)];
} else {
$addOptions = "ENGINE=InnoDB";
}

if(!isset($fields['ID'])) $fields['ID'] = "int(11) not null auto_increment";
if($fields) foreach($fields as $k => $v) $fieldSchemas .= "\"$k\" $v,\n";
if($indexes) foreach($indexes as $k => $v) $indexSchemas .= $this->getIndexSqlDefinition($k, $v) . ",\n";
Expand All @@ -221,7 +227,7 @@ public function createTable($table, $fields = null, $indexes = null, $options =
$indexSchemas
primary key (ID)
) {$addOptions}");

return $table;
}

Expand All @@ -235,6 +241,13 @@ public function createTable($table, $fields = null, $indexes = null, $options =
* @param $alteredOptions
*/
public function alterTable($tableName, $newFields = null, $newIndexes = null, $alteredFields = null, $alteredIndexes = null, $alteredOptions = null, $advancedOptions = null) {
if($this->isView($tableName)) {
DB::alteration_message(
sprintf("Table %s not changed as it is a view", $tableName),
"changed"
);
return;
}
$fieldSchemas = $indexSchemas = "";
$alterList = array();

Expand All @@ -257,7 +270,12 @@ public function alterTable($tableName, $newFields = null, $newIndexes = null, $a
);
}
}


public function isView($tableName) {
$info = $this->query("SHOW /*!50002 FULL*/ TABLES LIKE '$tableName'")->record();
return $info && strtoupper($info['Table_type']) == 'VIEW';
}

public function renameTable($oldTableName, $newTableName) {
$this->query("ALTER TABLE \"$oldTableName\" RENAME \"$newTableName\"");
}
Expand Down Expand Up @@ -513,7 +531,7 @@ public function tableList() {
* @return int
*/
public function affectedRows() {
return mysql_affected_rows($this->dbConn);
return $this->dbConn->affected_rows;
}

function databaseError($msg, $errorLevel = E_USER_ERROR) {
Expand Down Expand Up @@ -575,7 +593,8 @@ public function decimal($values){

$defaultValue = '';
if(isset($values['default']) && is_numeric($values['default'])) {
$defaultValue = ' default ' . $values['default'];
$decs = strpos($precision, ',') !== false ? (int)substr($precision, strpos($precision, ',')+1) : 0;
$defaultValue = ' default ' . number_format($values['default'], $decs, '.', '');
}

return 'decimal(' . $precision . ') not null' . $defaultValue;
Expand Down Expand Up @@ -877,7 +896,7 @@ function dbDataType($type){
* This will return text which has been escaped in a database-friendly manner.
*/
function addslashes($value){
return mysql_real_escape_string($value, $this->dbConn);
return $this->dbConn->real_escape_string($value);
}

/*
Expand Down Expand Up @@ -931,30 +950,43 @@ public function supportsExtensions($extensions=Array('partitions', 'tablespaces'
* See http://developer.postgresql.org/pgdocs/postgres/sql-set-transaction.html for details on transaction isolation options
*/
public function transactionStart($transaction_mode=false, $session_characteristics=false){
//Transactions not set up for MySQL yet
// This sets the isolation level for the NEXT transaction, not the current one.
if($transaction_mode) {
$this->query('SET TRANSACTION ' . $transaction_mode . ';');
}

$this->query('START TRANSACTION;');

if($session_characteristics) {
$this->query('SET SESSION TRANSACTION ' . $session_characteristics . ';');
}
}

/*
* Create a savepoint that you can jump back to if you encounter problems
*/
public function transactionSavepoint($savepoint){
//Transactions not set up for MySQL yet
$this->query("SAVEPOINT $savepoint;");
}

/*
* Rollback or revert to a savepoint if your queries encounter problems
* If you encounter a problem at any point during a transaction, you may
* need to rollback that particular query, or return to a savepoint
*/
public function transactionRollback($savepoint=false){
//Transactions not set up for MySQL yet
public function transactionRollback($savepoint = false){
if($savepoint) {
$this->query('ROLLBACK TO ' . $savepoint . ';');
} else {
$this->query('ROLLBACK');
}
}

/*
* Commit everything inside this transaction so far
*/
public function transactionEnd(){
//Transactions not set up for MySQL yet
public function transactionEnd($chain = false){
$this->query('COMMIT AND ' . ($chain ? '' : 'NO ') . 'CHAIN;');
}

/**
Expand Down Expand Up @@ -1098,35 +1130,30 @@ public function __construct(MySQLDatabase $database, $handle) {
}

public function __destruct() {
if(is_resource($this->handle)) mysql_free_result($this->handle);
if(is_object($this->handle)) $this->handle->free();
}

public function seek($row) {
return is_resource($this->handle) ? mysql_data_seek($this->handle, $row) : false;
if(is_object($this->handle)) return $this->handle->data_seek($row);
}

public function numRecords() {
return is_resource($this->handle) ? mysql_num_rows($this->handle) : false;
if(is_object($this->handle)) return $this->handle->num_rows;
}

public function nextRecord() {
// Coalesce rather than replace common fields.
if(is_resource($this->handle) && $data = mysql_fetch_row($this->handle)) {
foreach($data as $columnIdx => $value) {
$columnName = mysql_field_name($this->handle, $columnIdx);
// $value || !$ouput[$columnName] means that the *last* occurring value is shown
// !$ouput[$columnName] means that the *first* occurring value is shown
if(isset($value) || !isset($output[$columnName])) {
$output[$columnName] = $value;
if(is_object($this->handle) && $data = $this->handle->fetch_row()) {
$output = array();
$this->handle->field_seek(0);
while($field = $this->handle->fetch_field()) {
$columnName = $field->name;
if(isset($data[$this->handle->current_field-1]) || !isset($output[$columnName])) {
$output[$columnName] = $data[$this->handle->current_field-1];
}
}
return $output;
} else {
return false;
}
}


}

?>

2 comments on commit 6bf3f7d

@chillu
Copy link
Member

@chillu chillu commented on 6bf3f7d Oct 29, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sminnee @simonwelsh Awesome you've got that in, yay for using less deprecated APIs in SilverStripe :) This should also be tagged "API CHANGE". But more importantly: Let's try to document these relatively high impact changes at the time of commit, rather than release. Thats why we have /docs in source control now. So in a new /docs/en/changelog/alpha/3.0.0-alpha1.md file, write down an upgrading guide.

For example, what the "create table" changes mean for existing and new users and existing databases. What happens when you upgrade from 2.4 with an existing database, and then create a new subclass - will the new table(s) be in InnoDB, and everything else in MyISAM, hence potentially breaking any usage of MySQL FULLTEXT searches?

We should also create a script for migrating all tables to InnoDB, and explain why and when its a good idea (or point to a tech article that does it).

@powtac
Copy link
Contributor

@powtac powtac commented on 6bf3f7d Jan 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Please sign in to comment.