2009-02-25 06:44:52 +01:00
< ? php
2009-04-22 00:19:45 +02:00
/**
2010-05-28 00:25:45 +02:00
* Microsoft SQL Server 2008 + connector class .
2011-01-12 03:05:32 +01:00
*
2010-05-26 12:05:21 +02:00
* < h2 > Connecting using Windows </ h2 >
2011-01-12 03:05:32 +01:00
*
2009-10-22 06:02:40 +02:00
* If you 've got your website running on Windows, it' s highly recommended you
2010-05-26 12:05:21 +02:00
* use Microsoft SQL Server Driver for PHP " sqlsrv " .
2011-01-12 03:05:32 +01:00
*
2010-08-30 07:47:57 +02:00
* A complete guide to installing a Windows IIS + PHP + SQL Server web stack can be
* found here : http :// doc . silverstripe . org / installation - on - windows - server - manual - iis
2011-01-12 03:05:32 +01:00
*
2010-05-26 12:05:21 +02:00
* @ see http :// sqlsrvphp . codeplex . com /
2011-01-12 03:05:32 +01:00
*
2010-05-26 12:05:21 +02:00
* < h2 > Connecting using Linux or Mac OS X </ h2 >
2011-01-12 03:05:32 +01:00
*
2010-05-28 00:25:45 +02:00
* The following commands assume you used the default package manager
* to install PHP with the operating system .
2011-01-12 03:05:32 +01:00
*
2010-05-28 00:25:45 +02:00
* Debian , and Ubuntu :
* < code > apt - get install php5 - sybase </ code >
*
* Fedora , CentOS and RedHat :
* < code > yum install php - mssql </ code >
2011-01-12 03:05:32 +01:00
*
2010-05-28 00:25:45 +02:00
* Mac OS X ( MacPorts ) :
* < code > port install php5 - mssql </ code >
2011-01-12 03:05:32 +01:00
*
2010-05-28 00:25:45 +02:00
* These packages will install the mssql extension for PHP , as well
2010-08-30 07:47:57 +02:00
* as FreeTDS , which will let you connect to SQL Server .
2011-01-12 03:05:32 +01:00
*
2010-05-26 12:05:21 +02:00
* More information available in the SilverStripe developer wiki :
* @ see http :// doc . silverstripe . org / modules : mssql
2010-08-30 07:47:57 +02:00
* @ see http :// doc . silverstripe . org / installation - on - windows - server - manual - iis
2011-01-12 03:05:32 +01:00
*
2010-05-28 00:25:45 +02:00
* References :
* @ see http :// freetds . org
2011-01-12 03:05:32 +01:00
*
2009-10-22 06:01:17 +02:00
* @ package mssql
2009-04-22 00:19:45 +02:00
*/
2009-10-26 21:59:29 +01:00
class MSSQLDatabase extends SS_Database {
2011-01-12 03:05:32 +01:00
2009-02-25 06:44:52 +01:00
/**
2013-04-03 06:19:26 +02:00
* Words that will trigger an error if passed to a SQL Server fulltext search
2009-02-25 06:44:52 +01:00
*/
2013-04-03 06:19:26 +02:00
public static $noiseWords = array ( 'about' , '1' , 'after' , '2' , 'all' , 'also' , '3' , 'an' , '4' , 'and' , '5' , 'another' , '6' , 'any' , '7' , 'are' , '8' , 'as' , '9' , 'at' , '0' , 'be' , '$' , 'because' , 'been' , 'before' , 'being' , 'between' , 'both' , 'but' , 'by' , 'came' , 'can' , 'come' , 'could' , 'did' , 'do' , 'does' , 'each' , 'else' , 'for' , 'from' , 'get' , 'got' , 'has' , 'had' , 'he' , 'have' , 'her' , 'here' , 'him' , 'himself' , 'his' , 'how' , 'if' , 'in' , 'into' , 'is' , 'it' , 'its' , 'just' , 'like' , 'make' , 'many' , 'me' , 'might' , 'more' , 'most' , 'much' , 'must' , 'my' , 'never' , 'no' , 'now' , 'of' , 'on' , 'only' , 'or' , 'other' , 'our' , 'out' , 'over' , 're' , 'said' , 'same' , 'see' , 'should' , 'since' , 'so' , 'some' , 'still' , 'such' , 'take' , 'than' , 'that' , 'the' , 'their' , 'them' , 'then' , 'there' , 'these' , 'they' , 'this' , 'those' , 'through' , 'to' , 'too' , 'under' , 'up' , 'use' , 'very' , 'want' , 'was' , 'way' , 'we' , 'well' , 'were' , 'what' , 'when' , 'where' , 'which' , 'while' , 'who' , 'will' , 'with' , 'would' , 'you' , 'your' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' );
2011-01-12 03:05:32 +01:00
2009-02-25 06:44:52 +01:00
/**
2013-04-03 06:19:26 +02:00
* Transactions will work with FreeTDS , but not entirely with sqlsrv driver on Windows with MARS enabled .
* TODO :
* - after the test fails with open transaction , the transaction should be rolled back ,
* otherwise other tests will break claiming that transaction is still open .
* - figure out SAVEPOINTS
* - READ ONLY transactions
2009-02-25 06:44:52 +01:00
*/
2013-04-03 06:19:26 +02:00
protected $supportsTransactions = true ;
2011-01-12 03:05:32 +01:00
2009-02-25 06:44:52 +01:00
/**
2013-04-03 06:19:26 +02:00
* Cached flag to determine if full - text is enabled . This is set by
* { @ link MSSQLDatabase :: fullTextEnabled ()}
2011-01-12 03:05:32 +01:00
*
2013-04-03 06:19:26 +02:00
* @ var boolean
2010-02-07 02:59:13 +01:00
*/
2013-04-03 06:19:26 +02:00
protected $fullTextEnabled = null ;
2011-01-12 03:05:32 +01:00
2009-02-25 06:44:52 +01:00
/**
2013-04-03 06:19:26 +02:00
* Set the default collation of the MSSQL nvarchar fields that we create .
* We don ' t apply this to the database as a whole , so that we can use unicode collations .
*
* @ param string $collation
2009-02-25 06:44:52 +01:00
*/
2013-04-03 06:19:26 +02:00
public static function set_collation ( $collation ) {
Config :: inst () -> update ( 'MSSQLDatabase' , 'collation' , $collation );
2009-02-25 06:44:52 +01:00
}
2013-04-03 06:19:26 +02:00
2009-02-25 06:44:52 +01:00
/**
2013-04-03 06:19:26 +02:00
* The default collation of the MSSQL nvarchar fields that we create .
* We don ' t apply this to the database as a whole , so that we can use
* unicode collations .
*
2009-02-25 06:44:52 +01:00
* @ return string
*/
2013-04-03 06:19:26 +02:00
public static function get_collation () {
return Config :: inst () -> get ( 'MSSQLDatabase' , 'collation' );
2009-02-25 06:44:52 +01:00
}
2011-01-12 03:05:32 +01:00
2009-02-25 06:44:52 +01:00
/**
2013-04-03 06:19:26 +02:00
* Connect to a MS SQL database .
* @ param array $parameters An map of parameters , which should include :
* - server : The server , eg , localhost
* - username : The username to log on with
* - password : The password to log on with
* - database : The database to connect to
* - windowsauthentication : Set to true to use windows authentication
* instead of username / password
2009-02-25 06:44:52 +01:00
*/
2013-04-03 06:19:26 +02:00
public function connect ( $parameters ) {
parent :: connect ( $parameters );
2011-01-12 03:05:32 +01:00
2013-04-03 06:19:26 +02:00
// Configure the connection
$this -> query ( 'SET QUOTED_IDENTIFIER ON' );
$this -> query ( 'SET TEXTSIZE 2147483647' );
2009-02-25 06:44:52 +01:00
}
2011-01-12 03:05:32 +01:00
2009-02-25 06:44:52 +01:00
/**
2013-04-03 06:19:26 +02:00
* Checks whether the current SQL Server version has full - text
* support installed and full - text is enabled for this database .
2011-01-12 03:05:32 +01:00
*
2013-04-03 06:19:26 +02:00
* @ return boolean
2009-02-25 06:44:52 +01:00
*/
2013-04-03 06:19:26 +02:00
public function fullTextEnabled () {
if ( $this -> fullTextEnabled === null ) {
$this -> fullTextEnabled = $this -> updateFullTextEnabled ();
}
return $this -> fullTextEnabled ;
2009-02-25 06:44:52 +01:00
}
2013-04-03 06:19:26 +02:00
2009-02-25 06:44:52 +01:00
/**
2013-04-03 06:19:26 +02:00
* Checks whether the current SQL Server version has full - text
* support installed and full - text is enabled for this database .
2011-01-12 03:05:32 +01:00
*
2013-04-03 06:19:26 +02:00
* @ return boolean
2009-02-25 06:44:52 +01:00
*/
2013-04-03 06:19:26 +02:00
protected function updateFullTextEnabled () {
// Check if installed
$isInstalled = $this -> query ( " SELECT fulltextserviceproperty('isfulltextinstalled') " ) -> value ();
if ( ! $isInstalled ) return false ;
// Check if current database is enabled
$database = $this -> getSelectedDatabase ();
$enabledForDb = $this -> preparedQuery (
" SELECT is_fulltext_enabled FROM sys.databases WHERE name = ? " ,
array ( $database )
) -> value ();
return $enabledForDb ;
2009-02-25 06:44:52 +01:00
}
2011-01-12 03:05:32 +01:00
2013-04-03 06:19:26 +02:00
public function supportsCollations () {
return true ;
2009-02-25 06:44:52 +01:00
}
2010-05-19 12:48:19 +02:00
2013-04-03 06:19:26 +02:00
public function supportsTimezoneOverride () {
return true ;
2009-02-25 06:44:52 +01:00
}
2011-01-12 03:05:32 +01:00
2013-04-03 06:19:26 +02:00
public function getDatabaseServer () {
return " sqlsrv " ;
2009-03-12 00:04:01 +01:00
}
2013-04-03 06:19:26 +02:00
public function selectDatabase ( $name , $create = false , $errorLevel = E_USER_ERROR ) {
$this -> fullTextEnabled = null ;
return parent :: selectDatabase ( $name , $create , $errorLevel );
2009-02-25 06:44:52 +01:00
}
2011-01-12 03:05:32 +01:00
2013-04-03 06:19:26 +02:00
public function clearTable ( $table ) {
$this -> query ( " TRUNCATE TABLE \" $table\ " " );
2009-02-25 06:44:52 +01:00
}
2009-03-11 22:47:06 +01:00
/**
2010-02-03 08:53:08 +01:00
* SQL Server uses CURRENT_TIMESTAMP for the current date / time .
2009-03-11 22:47:06 +01:00
*/
2010-02-03 08:53:08 +01:00
function now () {
2009-03-11 22:47:06 +01:00
return 'CURRENT_TIMESTAMP' ;
}
2011-01-12 03:05:32 +01:00
2009-09-17 09:07:02 +02:00
/**
* Returns the database - specific version of the random () function
*/
2011-01-12 03:05:32 +01:00
function random (){
2009-09-17 09:07:02 +02:00
return 'RAND()' ;
2010-04-07 04:46:28 +02:00
}
2011-01-12 03:05:32 +01:00
2009-03-23 01:51:28 +01:00
/**
2009-07-20 11:09:53 +02:00
* The core search engine configuration .
2010-10-20 02:52:44 +02:00
* Picks up the fulltext - indexed tables from the database and executes search on all of them .
* Results are obtained as ID - ClassName pairs which is later used to reconstruct the DataObjectSet .
2011-01-12 03:05:32 +01:00
*
2010-10-20 02:52:44 +02:00
* @ param array classesToSearch computes all descendants and includes them . Check is done via WHERE clause .
2009-07-20 11:09:53 +02:00
* @ param string $keywords Keywords as a space separated string
* @ return object DataObjectSet of result pages
2009-03-23 01:51:28 +01:00
*/
2009-06-05 06:37:45 +02:00
public function searchEngine ( $classesToSearch , $keywords , $start , $pageLength , $sortBy = " Relevance DESC " , $extraFilter = " " , $booleanSearch = false , $alternativeFileFilter = " " , $invertedMatch = false ) {
2012-05-03 04:33:17 +02:00
if ( isset ( $objects )) $results = new ArrayList ( $objects );
else $results = new ArrayList ();
2011-12-17 00:43:09 +01:00
if ( ! $this -> fullTextEnabled ()) return $results ;
2010-11-01 04:20:52 +01:00
if ( ! in_array ( substr ( $sortBy , 0 , 9 ), array ( '"Relevanc' , 'Relevance' ))) user_error ( " Non-relevance sort not supported. " , E_USER_ERROR );
2010-10-20 02:52:44 +02:00
$allClassesToSearch = array ();
foreach ( $classesToSearch as $class ) {
2013-04-03 06:19:26 +02:00
$allClassesToSearch = array_merge ( $allClassesToSearch , array_values ( ClassInfo :: dataClassesFor ( $class )));
2010-10-20 02:52:44 +02:00
}
$allClassesToSearch = array_unique ( $allClassesToSearch );
2011-01-12 03:05:32 +01:00
2009-10-27 08:08:51 +01:00
//Get a list of all the tables and columns we'll be searching on:
2013-04-03 06:19:26 +02:00
$fulltextColumns = $this -> query ( 'EXEC sp_help_fulltext_columns' );
2010-08-31 07:52:48 +02:00
$queries = array ();
// Sort the columns back into tables.
2010-05-19 14:11:33 +02:00
$tables = array ();
2010-08-31 07:52:48 +02:00
foreach ( $fulltextColumns as $column ) {
// Skip extension tables.
2013-04-03 06:19:26 +02:00
if ( substr ( $column [ 'TABLE_NAME' ], - 5 ) == '_Live' || substr ( $column [ 'TABLE_NAME' ], - 9 ) == '_versions' ) continue ;
2010-08-31 07:52:48 +02:00
// Add the column to table.
$table = & $tables [ $column [ 'TABLE_NAME' ]];
2013-04-03 06:19:26 +02:00
if ( ! $table ) {
$table = array ( $column [ 'FULLTEXT_COLUMN_NAME' ]);
} else {
array_push ( $table , $column [ 'FULLTEXT_COLUMN_NAME' ]);
}
2010-08-31 07:52:48 +02:00
}
2010-10-20 02:52:44 +02:00
// Create one query per each table, $columns not used. We want just the ID and the ClassName of the object from this query.
2013-04-03 06:19:26 +02:00
foreach ( $tables as $tableName => $columns ){
2010-10-20 02:52:44 +02:00
$baseClass = ClassInfo :: baseDataClass ( $tableName );
2010-08-31 07:52:48 +02:00
$join = $this -> fullTextSearchMSSQL ( $tableName , $keywords );
2011-12-17 00:43:09 +01:00
if ( ! $join ) return $results ; // avoid "Null or empty full-text predicate"
2010-08-31 07:52:48 +02:00
// Check if we need to add ShowInSearch
$where = null ;
if ( strpos ( $tableName , 'SiteTree' ) === 0 ) {
$where = array ( " \" $tableName\ " . \ " ShowInSearch \" !=0 " );
2011-09-15 16:02:23 +02:00
} elseif ( strpos ( $tableName , 'File' ) === 0 ) {
2011-12-17 00:43:09 +01:00
// File.ShowInSearch was added later, keep the database driver backwards compatible
2011-09-15 16:02:23 +02:00
// by checking for its existence first
$fields = $this -> fieldList ( $tableName );
if ( array_key_exists ( 'ShowInSearch' , $fields )) {
$where = array ( " \" $tableName\ " . \ " ShowInSearch \" !=0 " );
}
2009-05-08 05:37:31 +02:00
}
2010-08-31 07:52:48 +02:00
2012-05-03 04:33:17 +02:00
$queries [ $tableName ] = DataList :: create ( $tableName ) -> where ( $where , '' ) -> dataQuery () -> query ();
2012-05-07 06:40:09 +02:00
$queries [ $tableName ] -> setOrderBy ( array ());
2010-10-20 02:52:44 +02:00
// Join with CONTAINSTABLE, a full text searcher that includes relevance factor
2012-05-07 06:40:09 +02:00
$queries [ $tableName ] -> setFrom ( array ( " \" $tableName\ " INNER JOIN $join AS \ " ft \" ON \" $tableName\ " . \ " ID \" = \" ft \" . \" KEY \" " ));
2010-10-20 02:52:44 +02:00
// Join with the base class if needed, as we want to test agains the ClassName
2011-12-17 00:43:09 +01:00
if ( $tableName != $baseClass ) {
2012-05-07 06:40:09 +02:00
$queries [ $tableName ] -> setFrom ( " INNER JOIN \" $baseClass\ " ON \ " $baseClass\ " . \ " ID \" = \" $tableName\ " . \ " ID \" " );
2010-10-20 02:52:44 +02:00
}
2012-05-03 04:33:17 +02:00
2012-05-07 06:40:09 +02:00
$queries [ $tableName ] -> setSelect ( array ( " \" $tableName\ " . \ " ID \" " ));
2012-05-03 04:33:17 +02:00
$queries [ $tableName ] -> selectField ( " ' $tableName ' " , 'Source' );
$queries [ $tableName ] -> selectField ( 'Rank' , 'Relevance' );
2010-10-20 02:52:44 +02:00
if ( $extraFilter ) {
2012-05-07 06:40:09 +02:00
$queries [ $tableName ] -> addWhere ( $extraFilter );
2010-10-20 02:52:44 +02:00
}
if ( count ( $allClassesToSearch )) {
2013-04-03 06:19:26 +02:00
$classesPlaceholder = DB :: placeholders ( $allClassesToSearch );
$queries [ $tableName ] -> addWhere ( array (
" \" $baseClass\ " . \ " ClassName \" IN ( $classesPlaceholder ) " =>
$allClassesToSearch
));
2010-10-20 02:52:44 +02:00
}
// Reset the parameters that would get in the way
2010-08-31 07:52:48 +02:00
}
2010-09-01 06:06:36 +02:00
// Generate SQL
2010-08-31 07:52:48 +02:00
$querySQLs = array ();
2013-04-03 06:19:26 +02:00
$queryParameters = array ();
2010-08-31 07:52:48 +02:00
foreach ( $queries as $query ) {
2013-04-03 06:19:26 +02:00
$querySQLs [] = $query -> sql ( $parameters );
$queryParameters = array_merge ( $queryParameters , $parameters );
2009-03-23 01:51:28 +01:00
}
2010-05-19 14:11:33 +02:00
2010-08-31 07:52:48 +02:00
// Unite the SQL
$fullQuery = implode ( " UNION " , $querySQLs ) . " ORDER BY $sortBy " ;
2010-05-19 14:11:33 +02:00
2010-08-31 07:52:48 +02:00
// Perform the search
2013-04-03 06:19:26 +02:00
$result = $this -> preparedQuery ( $fullQuery , $queryParameters );
2010-08-31 07:52:48 +02:00
2010-09-01 06:49:07 +02:00
// Regenerate DataObjectSet - watch out, numRecords doesn't work on sqlsrv driver on Windows.
2010-09-01 06:06:36 +02:00
$current = - 1 ;
2011-12-17 00:43:09 +01:00
$objects = array ();
2010-09-01 06:06:36 +02:00
foreach ( $result as $row ) {
$current ++ ;
2013-05-27 05:21:24 +02:00
// Select a subset for paging
if ( $current >= $start && $current < $start + $pageLength ) {
$objects [] = DataObject :: get_by_id ( $row [ 'Source' ], $row [ 'ID' ]);
}
2010-09-01 06:06:36 +02:00
}
2010-05-19 14:11:33 +02:00
2012-05-07 06:40:09 +02:00
if ( isset ( $objects )) $results = new ArrayList ( $objects );
else $results = new ArrayList ();
$list = new PaginatedList ( $results );
$list -> setPageStart ( $start );
$list -> setPageLength ( $pageLength );
$list -> setTotalItems ( $current + 1 );
return $list ;
2009-03-23 01:51:28 +01:00
}
2010-05-19 14:11:33 +02:00
2009-05-07 07:55:05 +02:00
/**
* Allow auto - increment primary key editing on the given table .
* Some databases need to enable this specially .
2013-04-03 06:19:26 +02:00
*
2009-05-07 07:55:05 +02:00
* @ param $table The name of the table to have PK editing allowed on
* @ param $allow True to start , false to finish
*/
function allowPrimaryKeyEditing ( $table , $allow = true ) {
$this -> query ( " SET IDENTITY_INSERT \" $table\ " " . ( $allow ? " ON " : " OFF " ));
}
2009-05-07 08:32:08 +02:00
/**
* Returns a SQL fragment for querying a fulltext search index
2010-08-31 07:52:48 +02:00
*
* @ param $tableName specific - table name
2009-05-07 08:32:08 +02:00
* @ param $keywords string The search query
2010-08-31 07:52:48 +02:00
* @ param $fields array The list of field names to search on , or null to include all
2010-09-01 00:19:58 +02:00
*
* @ returns null if keyword set is empty or the string with JOIN clause to be added to SQL query
2009-05-07 08:32:08 +02:00
*/
2013-04-03 06:19:26 +02:00
public function fullTextSearchMSSQL ( $tableName , $keywords , $fields = null ) {
2010-08-31 07:52:48 +02:00
// Make sure we are getting an array of fields
if ( isset ( $fields ) && ! is_array ( $fields )) $fields = array ( $fields );
// Strip unfriendly characters, SQLServer "CONTAINS" predicate will crash on & and | and ignore others anyway.
if ( function_exists ( 'mb_ereg_replace' )) {
$keywords = mb_ereg_replace ( '[^\w\s]' , '' , trim ( $keywords ));
2013-04-03 06:19:26 +02:00
} else {
$keywords = $this -> escapeString ( str_replace ( array ( '&' , '|' , '!' , '"' , '\'' ), '' , trim ( $keywords )));
2010-08-31 07:52:48 +02:00
}
2011-01-12 03:05:32 +01:00
2010-08-31 07:52:48 +02:00
// Remove stopwords, concat with ANDs
2013-04-03 06:19:26 +02:00
$keywordList = explode ( ' ' , $keywords );
$keywordList = $this -> removeStopwords ( $keywordList );
2012-12-18 23:19:21 +01:00
// remove any empty values from the array
2013-04-03 06:19:26 +02:00
$keywordList = array_filter ( $keywordList );
if ( empty ( $keywordList )) return null ;
2010-09-01 00:19:58 +02:00
2013-04-03 06:19:26 +02:00
$keywords = implode ( ' AND ' , $keywordList );
2010-08-31 07:52:48 +02:00
if ( $fields ) $fieldNames = '"' . implode ( '", "' , $fields ) . '"' ;
else $fieldNames = " * " ;
2009-05-07 08:32:08 +02:00
2012-04-26 07:31:12 +02:00
return " CONTAINSTABLE( \" $tableName\ " , ( $fieldNames ), '$keywords' ) " ;
2009-05-07 08:32:08 +02:00
}
2011-01-12 03:05:32 +01:00
2009-07-09 03:11:02 +02:00
/**
2010-08-31 07:52:48 +02:00
* Remove stopwords that would kill a MSSQL full - text query
2009-07-09 03:11:02 +02:00
*
2011-01-12 03:05:32 +01:00
* @ param array $keywords
2010-08-31 07:52:48 +02:00
*
* @ return array $keywords with stopwords removed
*/
2013-04-03 06:19:26 +02:00
public function removeStopwords ( $keywords ) {
2010-08-31 07:52:48 +02:00
$goodKeywords = array ();
foreach ( $keywords as $keyword ) {
if ( in_array ( $keyword , self :: $noiseWords )) continue ;
$goodKeywords [] = trim ( $keyword );
2009-07-09 03:11:02 +02:00
}
2010-08-31 07:52:48 +02:00
return $goodKeywords ;
2009-07-09 03:11:02 +02:00
}
2011-01-12 03:05:32 +01:00
2010-10-02 05:36:04 +02:00
/**
2009-10-01 23:11:18 +02:00
* Does this database support transactions ?
*/
public function supportsTransactions (){
return $this -> supportsTransactions ;
}
2011-01-12 03:05:32 +01:00
2010-10-02 05:36:04 +02:00
/**
2009-10-12 00:04:24 +02:00
* This is a quick lookup to discover if the database supports particular extensions
* Currently , MSSQL supports no extensions
2013-04-03 06:19:26 +02:00
*
* @ param array $extensions List of extensions to check for support of . The key of this array
* will be an extension name , and the value the configuration for that extension . This
* could be one of partitions , tablespaces , or clustering
* @ return boolean Flag indicating support for all of the above
2009-10-12 00:04:24 +02:00
*/
2013-04-03 06:19:26 +02:00
public function supportsExtensions ( $extensions = array ( 'partitions' , 'tablespaces' , 'clustering' )){
2009-10-12 00:04:24 +02:00
if ( isset ( $extensions [ 'partitions' ]))
return false ;
elseif ( isset ( $extensions [ 'tablespaces' ]))
return false ;
elseif ( isset ( $extensions [ 'clustering' ]))
return false ;
else
return false ;
}
2011-03-11 04:41:14 +01:00
2010-10-02 05:36:04 +02:00
/**
2010-08-31 05:18:00 +02:00
* Start transaction . READ ONLY not supported .
2009-10-01 23:11:18 +02:00
*/
2013-04-03 06:19:26 +02:00
public function transactionStart ( $transactionMode = false , $sessionCharacteristics = false ){
if ( $this -> connector instanceof SQLServerConnector ) {
$this -> connector -> transactionStart ();
2010-08-31 05:18:00 +02:00
} else {
2013-04-03 06:19:26 +02:00
$this -> query ( 'BEGIN TRANSACTION' );
2010-08-31 05:18:00 +02:00
}
2009-10-01 23:11:18 +02:00
}
2011-01-12 03:05:32 +01:00
2009-10-01 23:11:18 +02:00
public function transactionSavepoint ( $savepoint ){
2013-04-03 06:19:26 +02:00
$this -> query ( " SAVE TRANSACTION \" $savepoint\ " " );
2009-10-01 23:11:18 +02:00
}
2011-01-12 03:05:32 +01:00
2013-04-03 06:19:26 +02:00
public function transactionRollback ( $savepoint = false ) {
2010-06-24 07:37:45 +02:00
if ( $savepoint ) {
2013-04-03 06:19:26 +02:00
$this -> query ( " ROLLBACK TRANSACTION \" $savepoint\ " " );
} elseif ( $this -> connector instanceof SQLServerConnector ) {
$this -> connector -> transactionRollback ();
2010-06-24 07:37:45 +02:00
} else {
2013-04-03 06:19:26 +02:00
$this -> query ( 'ROLLBACK TRANSACTION' );
2010-06-24 07:37:45 +02:00
}
2009-10-01 23:11:18 +02:00
}
2011-03-11 04:41:14 +01:00
2013-04-03 06:19:26 +02:00
public function transactionEnd ( $chain = false ) {
if ( $this -> connector instanceof SQLServerConnector ) {
$this -> connector -> transactionEnd ();
2010-08-31 05:18:00 +02:00
} else {
2013-04-03 06:19:26 +02:00
$this -> query ( 'COMMIT TRANSACTION' );
2010-08-31 05:18:00 +02:00
}
2009-10-01 23:11:18 +02:00
}
2013-04-03 06:19:26 +02:00
public function comparisonClause ( $field , $value , $exact = false , $negate = false , $caseSensitive = null , $parameterised = false ) {
2012-12-11 01:47:35 +01:00
if ( $exact ) {
$comp = ( $negate ) ? '!=' : '=' ;
} else {
$comp = 'LIKE' ;
if ( $negate ) $comp = 'NOT ' . $comp ;
}
// Field definitions are case insensitive by default,
// change used collation for case sensitive searches.
$collateClause = '' ;
if ( $caseSensitive === true ) {
2013-04-03 06:19:26 +02:00
if ( self :: get_collation ()) {
$collation = preg_replace ( '/_CI_/' , '_CS_' , self :: get_collation ());
2012-12-11 01:47:35 +01:00
} else {
$collation = 'Latin1_General_CS_AS' ;
}
$collateClause = ' COLLATE ' . $collation ;
} elseif ( $caseSensitive === false ) {
2013-04-03 06:19:26 +02:00
if ( self :: get_collation ()) {
$collation = preg_replace ( '/_CS_/' , '_CI_' , self :: get_collation ());
2012-12-11 01:47:35 +01:00
} else {
$collation = 'Latin1_General_CI_AS' ;
}
$collateClause = ' COLLATE ' . $collation ;
}
2013-04-03 06:19:26 +02:00
$clause = sprintf ( " %s %s %s " , $field , $comp , $parameterised ? '?' : " ' $value ' " );
2012-12-11 01:47:35 +01:00
if ( $collateClause ) $clause .= $collateClause ;
return $clause ;
}
2010-02-03 06:01:37 +01:00
/**
2010-05-18 12:47:05 +02:00
* Function to return an SQL datetime expression for MSSQL
2010-02-03 06:01:37 +01:00
* used for querying a datetime in a certain format
2013-04-03 06:19:26 +02:00
*
2010-02-03 06:01:37 +01:00
* @ param string $date to be formated , can be either 'now' , literal datetime like '1973-10-14 10:30:00' or field name , e . g . '"SiteTree"."Created"'
* @ param string $format to be used , supported specifiers :
* % Y = Year ( four digits )
* % m = Month ( 01. . 12 )
* % d = Day ( 01. . 31 )
* % H = Hour ( 00. . 23 )
* % i = Minutes ( 00. . 59 )
* % s = Seconds ( 00. . 59 )
* % U = unix timestamp , can only be used on it ' s own
* @ return string SQL datetime expression to query for a formatted datetime
*/
function formattedDatetimeClause ( $date , $format ) {
preg_match_all ( '/%(.)/' , $format , $matches );
foreach ( $matches [ 1 ] as $match ) if ( array_search ( $match , array ( 'Y' , 'm' , 'd' , 'H' , 'i' , 's' , 'U' )) === false ) user_error ( 'formattedDatetimeClause(): unsupported format character %' . $match , E_USER_WARNING );
if ( preg_match ( '/^now$/i' , $date )) {
$date = " CURRENT_TIMESTAMP " ;
} else if ( preg_match ( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i' , $date )) {
2010-02-04 05:53:25 +01:00
$date = " ' $date .000' " ;
2010-02-03 06:01:37 +01:00
}
2010-09-29 12:10:51 +02:00
if ( $format == '%U' ) {
return " DATEDIFF(s, '1970-01-01 00:00:00', DATEADD(hour, DATEDIFF(hour, GETDATE(), GETUTCDATE()), $date )) " ;
}
2010-02-03 06:01:37 +01:00
$trans = array (
'Y' => 'yy' ,
'm' => 'mm' ,
'd' => 'dd' ,
'H' => 'hh' ,
'i' => 'mi' ,
's' => 'ss' ,
);
$strings = array ();
$buffer = $format ;
while ( strlen ( $buffer )) {
if ( substr ( $buffer , 0 , 1 ) == '%' ) {
$f = substr ( $buffer , 1 , 1 );
$flen = $f == 'Y' ? 4 : 2 ;
$strings [] = " RIGHT('0' + CAST(DATEPART( { $trans [ $f ] } , $date ) AS VARCHAR), $flen ) " ;
$buffer = substr ( $buffer , 2 );
} else {
$pos = strpos ( $buffer , '%' );
if ( $pos === false ) {
$strings [] = $buffer ;
$buffer = '' ;
} else {
$strings [] = " ' " . substr ( $buffer , 0 , $pos ) . " ' " ;
$buffer = substr ( $buffer , $pos );
}
}
}
return '(' . implode ( ' + ' , $strings ) . ')' ;
2011-01-12 03:05:32 +01:00
2010-02-03 06:01:37 +01:00
}
2011-01-12 03:05:32 +01:00
2010-02-03 06:01:37 +01:00
/**
2010-05-18 12:47:05 +02:00
* Function to return an SQL datetime expression for MSSQL .
2010-02-03 06:01:37 +01:00
* used for querying a datetime addition
2013-04-03 06:19:26 +02:00
*
2010-02-03 06:01:37 +01:00
* @ param string $date , can be either 'now' , literal datetime like '1973-10-14 10:30:00' or field name , e . g . '"SiteTree"."Created"'
* @ param string $interval to be added , use the format [ sign ][ integer ] [ qualifier ], e . g . - 1 Day , + 15 minutes , + 1 YEAR
* supported qualifiers :
* - years
* - months
* - days
* - hours
* - minutes
* - seconds
* This includes the singular forms as well
* @ return string SQL datetime expression to query for a datetime ( YYYY - MM - DD hh : mm : ss ) which is the result of the addition
*/
function datetimeIntervalClause ( $date , $interval ) {
$trans = array (
'year' => 'yy' ,
'month' => 'mm' ,
'day' => 'dd' ,
'hour' => 'hh' ,
'minute' => 'mi' ,
'second' => 'ss' ,
);
$singularinterval = preg_replace ( '/(year|month|day|hour|minute|second)s/i' , '$1' , $interval );
if (
! ( $params = preg_match ( '/([-+]\d+) (\w+)/i' , $singularinterval , $matches )) ||
! isset ( $trans [ strtolower ( $matches [ 2 ])])
) user_error ( 'datetimeIntervalClause(): invalid interval ' . $interval , E_USER_WARNING );
if ( preg_match ( '/^now$/i' , $date )) {
$date = " CURRENT_TIMESTAMP " ;
} else if ( preg_match ( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i' , $date )) {
$date = " ' $date ' " ;
}
2010-05-19 12:09:51 +02:00
return " CONVERT(VARCHAR, DATEADD( " . $trans [ strtolower ( $matches [ 2 ])] . " , " . ( int ) $matches [ 1 ] . " , $date ), 120) " ;
2010-02-03 06:01:37 +01:00
}
/**
2010-05-18 12:47:05 +02:00
* Function to return an SQL datetime expression for MSSQL .
2010-02-03 06:01:37 +01:00
* used for querying a datetime substraction
2013-04-03 06:19:26 +02:00
*
2010-02-03 06:01:37 +01:00
* @ param string $date1 , can be either 'now' , literal datetime like '1973-10-14 10:30:00' or field name , e . g . '"SiteTree"."Created"'
* @ param string $date2 to be substracted of $date1 , can be either 'now' , literal datetime like '1973-10-14 10:30:00' or field name , e . g . '"SiteTree"."Created"'
* @ return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which is the result of the substraction
*/
function datetimeDifferenceClause ( $date1 , $date2 ) {
if ( preg_match ( '/^now$/i' , $date1 )) {
$date1 = " CURRENT_TIMESTAMP " ;
} else if ( preg_match ( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i' , $date1 )) {
$date1 = " ' $date1 ' " ;
}
if ( preg_match ( '/^now$/i' , $date2 )) {
$date2 = " CURRENT_TIMESTAMP " ;
} else if ( preg_match ( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i' , $date2 )) {
$date2 = " ' $date2 ' " ;
}
return " DATEDIFF(s, $date2 , $date1 ) " ;
}
2009-02-25 06:44:52 +01:00
}