Skip to content

Commit

Permalink
Merge pull request #84 from simonwelsh/master
Browse files Browse the repository at this point in the history
MySQLi changes
  • Loading branch information
sminnee committed Oct 29, 2011
2 parents d88b39f + c8f1379 commit dc9b500
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 60 deletions.
15 changes: 14 additions & 1 deletion docs/en/topics/search.md
Expand Up @@ -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).
Expand Down
4 changes: 4 additions & 0 deletions filesystem/File.php
Expand Up @@ -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()}.
*
Expand Down
2 changes: 1 addition & 1 deletion model/DataObject.php
Expand Up @@ -3111,7 +3111,7 @@ static function enable_subclass_access() {
* @var array
*/
static $create_table_options = array(
'MySQLDatabase' => 'ENGINE=MyISAM'
'MySQLDatabase' => 'ENGINE=InnoDB'
);

/**
Expand Down
137 changes: 82 additions & 55 deletions model/MySQLDatabase.php
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;
}
}


}

?>
6 changes: 3 additions & 3 deletions tests/model/TransactionTest.php
Expand Up @@ -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:
Expand All @@ -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');
}
Expand Down

0 comments on commit dc9b500

Please sign in to comment.