Browse files

Merge pull request #84 from simonwelsh/master

MySQLi changes
  • Loading branch information...
2 parents d88b39f + c8f1379 commit dc9b50015642e4937cb295e0f674441cfbe4ecd0 @sminnee sminnee committed Oct 29, 2011
Showing with 104 additions and 60 deletions.
  1. +14 −1 docs/en/topics/search.md
  2. +4 −0 filesystem/File.php
  3. +1 −1 model/DataObject.php
  4. +82 −55 model/MySQLDatabase.php
  5. +3 −3 tests/model/TransactionTest.php
View
15 docs/en/topics/search.md
@@ -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
4 filesystem/File.php
@@ -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
2 model/DataObject.php
@@ -3111,7 +3111,7 @@ static function enable_subclass_access() {
* @var array
*/
static $create_table_options = array(
- 'MySQLDatabase' => 'ENGINE=MyISAM'
+ 'MySQLDatabase' => 'ENGINE=InnoDB'
);
/**
View
137 model/MySQLDatabase.php
@@ -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 @@ 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'");
}
/**
@@ -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;
}
/**
@@ -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;
}
/**
@@ -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;
}
/**
@@ -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";
@@ -221,7 +227,7 @@ public function createTable($table, $fields = null, $indexes = null, $options =
$indexSchemas
primary key (ID)
) {$addOptions}");
-
+
return $table;
}
@@ -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();
@@ -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\"");
}
@@ -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) {
@@ -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;
@@ -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);
}
/*
@@ -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;');
}
/**
@@ -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;
}
}
-
-
}
-
-?>
View
6 tests/model/TransactionTest.php
@@ -29,7 +29,7 @@ function testCreateWithTransaction() {
$obj->write();
$obj=new TransactionTest_Object();
- $obj->Title='Forth page';
+ $obj->Title='Fourth page';
$obj->write();
//Revert to a savepoint:
@@ -40,15 +40,15 @@ function testCreateWithTransaction() {
$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());
$this->assertTrue(is_object($second) && $second->exists());
//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');
}

0 comments on commit dc9b500

Please sign in to comment.