Merge pull request #84 from simonwelsh/master

MySQLi changes
This commit is contained in:
Sam Minnée 2011-10-28 18:54:31 -07:00
commit dc9b500156
5 changed files with 105 additions and 61 deletions

View File

@ -5,13 +5,26 @@
Fulltext search for page content (and other attributes like "Title" or "MetaTags") can be easily added to SilverStripe.
See [Tutorial: Site Search](/tutorials/4-site-search) for details.
## Searching for DataObject's
## Searching for DataObjects
The `[api:SearchContext]` class provides a good base implementation that you can hook into your own controllers.
A working implementation of searchable DataObjects can be seen in the `[api:ModelAdmin]` class.
[SearchContext](/reference/searchcontext) goes into more detail about setting up a default search form for `[api:DataObject]`s.
### Fulltext search on DataObjects
The `[api:MySQLDatabase]` class now defaults to creating tables using the InnoDB storage engine. As Fulltext search in MySQL
requires the MyISAM storage engine, any DataObject you wish to use with Fulltext search must be changed to use MyISAM storage
engine.
You can do so by adding this static variable to your class definition:
:::php
static $create_table_options = array(
'MySQLDatabase' => 'ENGINE=MyISAM'
);
## Searching for Documents
SilverStripe does not have a built-in method to search through file content (e.g. in PDF or DOC format).

View File

@ -97,6 +97,10 @@ class File extends DataObject {
"Hierarchy",
);
static $create_table_options = array(
'MySQLDatabase' => 'ENGINE=MyISAM'
);
/**
* @var array List of allowed file extensions, enforced through {@link validate()}.
*

View File

@ -3111,7 +3111,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @var array
*/
static $create_table_options = array(
'MySQLDatabase' => 'ENGINE=MyISAM'
'MySQLDatabase' => 'ENGINE=InnoDB'
);
/**

View File

@ -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.
@ -54,22 +54,22 @@ class MySQLDatabase extends SS_Database {
* - 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'");
}
/**
@ -84,7 +84,7 @@ class MySQLDatabase extends SS_Database {
* @return boolean
*/
public function supportsCollations() {
return $this->getVersion() >= 4.1;
return true;
}
/**
@ -92,7 +92,7 @@ class MySQLDatabase extends SS_Database {
* @return string
*/
public function getVersion() {
return mysql_get_server_info($this->dbConn);
return $this->dbConn->server_info;
}
/**
@ -113,19 +113,19 @@ class MySQLDatabase extends SS_Database {
$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() {
@ -133,15 +133,13 @@ class MySQLDatabase extends SS_Database {
}
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;
}
/**
@ -173,10 +171,12 @@ class MySQLDatabase extends SS_Database {
*/
public function selectDatabase($dbname) {
$this->database = $dbname;
if($this->databaseExists($this->database)) {
if(mysql_select_db($this->database, $this->dbConn)) $this->active = true;
}
$this->tableList = $this->fieldList = $this->indexList = null;
$this->active = false;
if($this->databaseExists($this->database)) {
$this->active = $this->dbConn->select_db($this->database);
}
return $this->active;
}
/**
@ -206,9 +206,15 @@ class MySQLDatabase extends SS_Database {
*/
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";
@ -221,7 +227,7 @@ class MySQLDatabase extends SS_Database {
$indexSchemas
primary key (ID)
) {$addOptions}");
return $table;
}
@ -235,6 +241,13 @@ class MySQLDatabase extends SS_Database {
* @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();
@ -257,7 +270,12 @@ class MySQLDatabase extends SS_Database {
);
}
}
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\"");
}
@ -513,7 +531,7 @@ class MySQLDatabase extends SS_Database {
* @return int
*/
public function affectedRows() {
return mysql_affected_rows($this->dbConn);
return $this->dbConn->affected_rows;
}
function databaseError($msg, $errorLevel = E_USER_ERROR) {
@ -575,7 +593,8 @@ class MySQLDatabase extends SS_Database {
$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;
@ -877,7 +896,7 @@ class MySQLDatabase extends SS_Database {
* 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);
}
/*
@ -931,14 +950,23 @@ class MySQLDatabase extends SS_Database {
* 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;");
}
/*
@ -946,15 +974,19 @@ class MySQLDatabase extends SS_Database {
* 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;');
}
/**
@ -1098,26 +1130,25 @@ class MySQLQuery extends SS_Query {
}
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;
@ -1125,8 +1156,4 @@ class MySQLQuery extends SS_Query {
return false;
}
}
}
?>

View File

@ -29,7 +29,7 @@ class TransactionTest extends SapphireTest {
$obj->write();
$obj=new TransactionTest_Object();
$obj->Title='Forth page';
$obj->Title='Fourth page';
$obj->write();
//Revert to a savepoint:
@ -40,7 +40,7 @@ class TransactionTest extends SapphireTest {
$first=DataObject::get('TransactionTest_Object', "\"Title\"='First page'");
$second=DataObject::get('TransactionTest_Object', "\"Title\"='Second page'");
$third=DataObject::get('TransactionTest_Object', "\"Title\"='Third page'");
$forth=DataObject::get('TransactionTest_Object', "\"Title\"='Forth page'");
$fourth=DataObject::get('TransactionTest_Object', "\"Title\"='Fourth page'");
//These pages should be in the system
$this->assertTrue(is_object($first) && $first->exists());
@ -48,7 +48,7 @@ class TransactionTest extends SapphireTest {
//These pages should NOT exist, we reverted to a savepoint:
$this->assertFalse(is_object($third) && $third->exists());
$this->assertFalse(is_object($forth) && $forth->exists());
$this->assertFalse(is_object($fourth) && $fourth->exists());
} else {
$this->markTestSkipped('Current database does not support transactions');
}