mirror of
https://github.com/silverstripe/silverstripe-mssql
synced 2024-10-22 08:05:53 +02:00
MINOR: MSSQL stub function created for Postgres-specific functionality
This commit is contained in:
parent
c104222019
commit
7ea6652e56
@ -1,69 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* Microsoft SQL Server 2008+ connector class.
|
||||
*
|
||||
*
|
||||
* <h2>Connecting using Windows</h2>
|
||||
*
|
||||
*
|
||||
* If you've got your website running on Windows, it's highly recommended you
|
||||
* use Microsoft SQL Server Driver for PHP "sqlsrv".
|
||||
*
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*
|
||||
* @see http://sqlsrvphp.codeplex.com/
|
||||
*
|
||||
*
|
||||
* <h2>Connecting using Linux or Mac OS X</h2>
|
||||
*
|
||||
*
|
||||
* The following commands assume you used the default package manager
|
||||
* to install PHP with the operating system.
|
||||
*
|
||||
*
|
||||
* Debian, and Ubuntu:
|
||||
* <code>apt-get install php5-sybase</code>
|
||||
*
|
||||
* Fedora, CentOS and RedHat:
|
||||
* <code>yum install php-mssql</code>
|
||||
*
|
||||
*
|
||||
* Mac OS X (MacPorts):
|
||||
* <code>port install php5-mssql</code>
|
||||
*
|
||||
*
|
||||
* These packages will install the mssql extension for PHP, as well
|
||||
* as FreeTDS, which will let you connect to SQL Server.
|
||||
*
|
||||
*
|
||||
* More information available in the SilverStripe developer wiki:
|
||||
* @see http://doc.silverstripe.org/modules:mssql
|
||||
* @see http://doc.silverstripe.org/installation-on-windows-server-manual-iis
|
||||
*
|
||||
*
|
||||
* References:
|
||||
* @see http://freetds.org
|
||||
*
|
||||
*
|
||||
* @package mssql
|
||||
*/
|
||||
class MSSQLDatabase extends SS_Database {
|
||||
|
||||
|
||||
/**
|
||||
* Connection to the DBMS.
|
||||
* @var resource
|
||||
*/
|
||||
protected $dbConn;
|
||||
|
||||
|
||||
/**
|
||||
* True if we are connected to a database.
|
||||
* @var boolean
|
||||
*/
|
||||
protected $active;
|
||||
|
||||
|
||||
/**
|
||||
* The name of the database.
|
||||
* @var string
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
|
||||
/**
|
||||
* If true, use the mssql_... functions.
|
||||
* If false use the sqlsrv_... functions
|
||||
*/
|
||||
protected $mssql = null;
|
||||
|
||||
|
||||
/**
|
||||
* Stores the affected rows of the last query.
|
||||
* Used by sqlsrv functions only, as sqlsrv_rows_affected
|
||||
@ -79,17 +79,17 @@ class MSSQLDatabase extends SS_Database {
|
||||
/**
|
||||
* 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,
|
||||
* - 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
|
||||
*/
|
||||
protected $supportsTransactions = true;
|
||||
|
||||
|
||||
/**
|
||||
* Cached flag to determine if full-text is enabled. This is set by
|
||||
* {@link MSSQLDatabase::fullTextEnabled()}
|
||||
*
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $fullTextEnabled = null;
|
||||
@ -112,7 +112,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
public static function set_collation($collation) {
|
||||
self::$collation = $collation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connect to a MS SQL database.
|
||||
* @param array $parameters An map of parameters, which should include:
|
||||
@ -163,7 +163,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
$this->query('SET TEXTSIZE 2147483647');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function __destruct() {
|
||||
if(is_resource($this->dbConn)) {
|
||||
if($this->mssql) {
|
||||
@ -173,11 +173,11 @@ class MSSQLDatabase extends SS_Database {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the current SQL Server version has full-text
|
||||
* support installed and full-text is enabled for this database.
|
||||
*
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function fullTextEnabled() {
|
||||
@ -192,7 +192,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
}
|
||||
return $this->fullTextEnabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Throw a database error
|
||||
*/
|
||||
@ -205,18 +205,18 @@ class MSSQLDatabase extends SS_Database {
|
||||
}
|
||||
$message .= ": \n" . implode("; ",$errorMessages);
|
||||
}
|
||||
|
||||
|
||||
return parent::databaseError($message, $errorLevel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This will set up the full text search capabilities.
|
||||
*/
|
||||
function createFullTextCatalog() {
|
||||
$result = $this->query("SELECT name FROM sys.fulltext_catalogs WHERE name = 'ftCatalog';")->value();
|
||||
if(!$result) $this->query("CREATE FULLTEXT CATALOG ftCatalog AS DEFAULT;");
|
||||
}
|
||||
|
||||
$result = $this->query("SELECT name FROM sys.fulltext_catalogs WHERE name = 'ftCatalog';")->value();
|
||||
if(!$result) $this->query("CREATE FULLTEXT CATALOG ftCatalog AS DEFAULT;");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep until the catalog has been fully rebuilt. This is a busy wait designed for situations
|
||||
* when you need to be sure the index is up to date - for example in unit tests.
|
||||
@ -229,12 +229,12 @@ class MSSQLDatabase extends SS_Database {
|
||||
function waitUntilIndexingFinished($maxWaitingTime = 15) {
|
||||
if($this->fullTextEnabled()) {
|
||||
$this->query("EXEC sp_fulltext_catalog 'ftCatalog', 'Rebuild';");
|
||||
|
||||
|
||||
// Busy wait until it's done updating, but no longer than 15 seconds.
|
||||
$start = time();
|
||||
while(time()-$start<$maxWaitingTime) {
|
||||
$status = $this->query("EXEC sp_help_fulltext_catalogs 'ftCatalog';")->first();
|
||||
|
||||
|
||||
if (isset($status['STATUS']) && $status['STATUS']==0) {
|
||||
// Idle!
|
||||
break;
|
||||
@ -243,14 +243,14 @@ class MSSQLDatabase extends SS_Database {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Not implemented, needed for PDO
|
||||
*/
|
||||
public function getConnect($parameters) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this database supports collations
|
||||
* @return boolean
|
||||
@ -258,7 +258,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
public function supportsCollations() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the version of MSSQL.
|
||||
* @return string
|
||||
@ -266,7 +266,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
public function getVersion() {
|
||||
return trim($this->query("SELECT CONVERT(char(15), SERVERPROPERTY('ProductVersion'))")->value());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the database server, namely mssql.
|
||||
* @return string
|
||||
@ -274,14 +274,14 @@ class MSSQLDatabase extends SS_Database {
|
||||
public function getDatabaseServer() {
|
||||
return "mssql";
|
||||
}
|
||||
|
||||
|
||||
public function query($sql, $errorLevel = E_USER_ERROR) {
|
||||
if(isset($_REQUEST['previewwrite']) && in_array(strtolower(substr($sql,0,strpos($sql,' '))), array('insert','update','delete','replace'))) {
|
||||
Debug::message("Will execute: $sql");
|
||||
return;
|
||||
}
|
||||
|
||||
if(isset($_REQUEST['showqueries'])) {
|
||||
if(isset($_REQUEST['showqueries'])) {
|
||||
$starttime = microtime(true);
|
||||
}
|
||||
|
||||
@ -310,7 +310,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
if(!$handle && $errorLevel) $this->databaseError("Couldn't run query ($error): $sql", $errorLevel);
|
||||
return new MSSQLQuery($this, $handle, $this->mssql);
|
||||
}
|
||||
|
||||
|
||||
public function getGeneratedID($table) {
|
||||
return $this->query("SELECT IDENT_CURRENT('$table')")->value();
|
||||
}
|
||||
@ -322,8 +322,8 @@ class MSSQLDatabase extends SS_Database {
|
||||
* @param string $tableName Name of table with primary key column "ID"
|
||||
* @return string Internal identifier for primary key
|
||||
*/
|
||||
function getPrimaryKey($tableName) {
|
||||
$indexes = DB::query("EXEC sp_helpindex '$tableName';");
|
||||
function getPrimaryKey($tableName){
|
||||
$indexes=DB::query("EXEC sp_helpindex '$tableName';");
|
||||
$indexName = '';
|
||||
foreach($indexes as $index) {
|
||||
if($index['index_keys'] == 'ID') {
|
||||
@ -348,11 +348,11 @@ class MSSQLDatabase extends SS_Database {
|
||||
TABLE_NAME = '$tableName'
|
||||
")->value();
|
||||
}
|
||||
|
||||
|
||||
public function isActive() {
|
||||
return $this->active ? true : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the database that is currently selected.
|
||||
*/
|
||||
@ -387,18 +387,18 @@ class MSSQLDatabase extends SS_Database {
|
||||
public function currentDatabase() {
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switches to the given database.
|
||||
*
|
||||
*
|
||||
* If the database doesn't exist, you should call
|
||||
* createDatabase() after calling selectDatabase()
|
||||
*
|
||||
*
|
||||
* @param string $dbname The database name to switch to
|
||||
*/
|
||||
public function selectDatabase($dbname) {
|
||||
$this->database = $dbname;
|
||||
|
||||
|
||||
if($this->databaseExists($this->database)) {
|
||||
if($this->mssql) {
|
||||
if(mssql_select_db($this->database, $this->dbConn)) {
|
||||
@ -409,7 +409,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
$this->active = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->tableList = $this->fieldList = $this->indexList = $this->fullTextEnabled = null;
|
||||
}
|
||||
|
||||
@ -422,9 +422,9 @@ class MSSQLDatabase extends SS_Database {
|
||||
$databases = $this->allDatabaseNames();
|
||||
foreach($databases as $dbname) {
|
||||
if($dbname == $name) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all databases names from the server.
|
||||
@ -447,26 +447,26 @@ class MSSQLDatabase extends SS_Database {
|
||||
public function createTable($tableName, $fields = null, $indexes = null, $options = null, $advancedOptions = null) {
|
||||
$fieldSchemas = $indexSchemas = "";
|
||||
if($fields) foreach($fields as $k => $v) $fieldSchemas .= "\"$k\" $v,\n";
|
||||
|
||||
|
||||
// Temporary tables start with "#" in MSSQL-land
|
||||
if(!empty($options['temporary'])) {
|
||||
// Randomize the temp table name to avoid conflicts in the tempdb table which derived databases share
|
||||
$tableName = "#$tableName" . '-' . rand(1000000, 9999999);
|
||||
}
|
||||
|
||||
|
||||
$this->query("CREATE TABLE \"$tableName\" (
|
||||
$fieldSchemas
|
||||
primary key (\"ID\")
|
||||
);");
|
||||
|
||||
|
||||
//we need to generate indexes like this: CREATE INDEX IX_vault_to_export ON vault (to_export);
|
||||
//This needs to be done AFTER the table creation, so we can set up the fulltext indexes correctly
|
||||
if($indexes) foreach($indexes as $k => $v) {
|
||||
$indexSchemas .= $this->getIndexSqlDefinition($tableName, $k, $v) . "\n";
|
||||
}
|
||||
|
||||
|
||||
if($indexSchemas) $this->query($indexSchemas);
|
||||
|
||||
|
||||
return $tableName;
|
||||
}
|
||||
|
||||
@ -482,7 +482,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
$fieldSchemas = $indexSchemas = "";
|
||||
$alterList = array();
|
||||
$indexList = $this->indexList($tableName);
|
||||
|
||||
|
||||
if($newFields) foreach($newFields as $k => $v) $alterList[] .= "ALTER TABLE \"$tableName\" ADD \"$k\" $v";
|
||||
|
||||
if($alteredFields) {
|
||||
@ -496,7 +496,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
if($val != '') $alterList[] .= $val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($alteredIndexes) foreach($alteredIndexes as $k => $v) $alterList[] .= $this->getIndexSqlDefinition($tableName, $k, $v);
|
||||
if($newIndexes) foreach($newIndexes as $k =>$v) $alterList[] .= $this->getIndexSqlDefinition($tableName, $k, $v);
|
||||
|
||||
@ -568,7 +568,8 @@ class MSSQLDatabase extends SS_Database {
|
||||
and parent_obj= OBJECT_ID('$tableName')
|
||||
and c.name = '$colName'")->value();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get enum values from a constraint check clause.
|
||||
* @param string $clause Check clause to parse values from
|
||||
@ -581,14 +582,15 @@ class MSSQLDatabase extends SS_Database {
|
||||
$bits = preg_split('/ *= */', $segment);
|
||||
for($i = 1; $i < sizeof($bits); $i += 2) {
|
||||
array_unshift($constraints, substr(rtrim($bits[$i], ')'), 1, -1));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Creates an ALTER expression for a column in MS SQL
|
||||
*
|
||||
*
|
||||
* @param $tableName Name of the table to be altered
|
||||
* @param $colName Name of the column to be altered
|
||||
* @param $colSpec String which contains conditions for a column
|
||||
@ -602,32 +604,32 @@ class MSSQLDatabase extends SS_Database {
|
||||
$pattern = '/^([\w()]+)\s?((?:not\s)?null)?\s?(default\s[\w\']+)?\s?(check\s?[\w()\'",\s]+)?$/i';
|
||||
$matches=Array();
|
||||
preg_match($pattern, $colSpec, $matches);
|
||||
|
||||
|
||||
// drop the index if it exists
|
||||
$alterCol='';
|
||||
$indexName = isset($indexList[$colName]['indexname']) ? $indexList[$colName]['indexname'] : null;
|
||||
if($indexName && $colName != 'ID') {
|
||||
$alterCol = "\nDROP INDEX \"$indexName\" ON \"$tableName\";";
|
||||
}
|
||||
|
||||
|
||||
$prefix="ALTER TABLE \"" . $tableName . "\" ";
|
||||
|
||||
// Remove the old default prior to adjusting the column.
|
||||
if($defaultConstraintName = $this->defaultConstraintName($tableName, $colName)) {
|
||||
$alterCol .= ";\n$prefix DROP CONSTRAINT \"$defaultConstraintName\"";
|
||||
}
|
||||
|
||||
|
||||
if(isset($matches[1])) {
|
||||
//We will prevent any changes being made to the ID column. Primary key indexes will have a fit if we do anything here.
|
||||
if($colName!='ID'){
|
||||
$alterCol .= ";\n$prefix ALTER COLUMN \"$colName\" $matches[1]";
|
||||
|
||||
|
||||
// SET null / not null
|
||||
if(!empty($matches[2])) $alterCol .= ";\n$prefix ALTER COLUMN \"$colName\" $matches[1] $matches[2]";
|
||||
|
||||
|
||||
// Add a default back
|
||||
if(!empty($matches[3])) $alterCol .= ";\n$prefix ADD $matches[3] FOR \"$colName\"";
|
||||
|
||||
|
||||
// SET check constraint (The constraint HAS to be dropped)
|
||||
if(!empty($matches[4])) {
|
||||
$constraint = $this->getConstraintName($tableName, $colName);
|
||||
@ -637,31 +639,33 @@ class MSSQLDatabase extends SS_Database {
|
||||
|
||||
//NOTE: 'with nocheck' seems to solve a few problems I've been having for modifying existing tables.
|
||||
$alterCol .= ";\n$prefix WITH NOCHECK ADD CONSTRAINT \"{$tableName}_{$colName}_check\" $matches[4]";
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return isset($alterCol) ? $alterCol : '';
|
||||
}
|
||||
|
||||
|
||||
public function renameTable($oldTableName, $newTableName) {
|
||||
$this->query("EXEC sp_rename \"$oldTableName\", \"$newTableName\"");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks a table's integrity and repairs it if necessary.
|
||||
* NOTE: MSSQL does not appear to support any vacuum or optimise commands
|
||||
*
|
||||
*
|
||||
* @var string $tableName The name of the table.
|
||||
* @return boolean Return true if the table has integrity after the method is complete.
|
||||
*/
|
||||
public function checkAndRepairTable($tableName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function createField($tableName, $fieldName, $fieldSpec) {
|
||||
$this->query("ALTER TABLE \"$tableName\" ADD \"$fieldName\" $fieldSpec");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Change the database type of the given field.
|
||||
* @param string $tableName The name of the tbale the field is in.
|
||||
@ -674,14 +678,14 @@ class MSSQLDatabase extends SS_Database {
|
||||
|
||||
/**
|
||||
* Change the database column name of the given field.
|
||||
*
|
||||
*
|
||||
* @param string $tableName The name of the tbale the field is in.
|
||||
* @param string $oldName The name of the field to change.
|
||||
* @param string $newName The new name of the field
|
||||
*/
|
||||
public function renameField($tableName, $oldName, $newName) {
|
||||
$this->query("EXEC sp_rename @objname = '$tableName.$oldName', @newname = '$newName', @objtype = 'COLUMN'");
|
||||
}
|
||||
$this->query("EXEC sp_rename @objname = '$tableName.$oldName', @newname = '$newName', @objtype = 'COLUMN'");
|
||||
}
|
||||
|
||||
public function fieldList($table) {
|
||||
//This gets us more information than we need, but I've included it all for the moment....
|
||||
@ -697,7 +701,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
foreach($fieldRecords as $record) {
|
||||
$fields[] = $record;
|
||||
}
|
||||
|
||||
|
||||
foreach($fields as $field) {
|
||||
// Update the data_type field to be a complete column definition string for use by
|
||||
// SS_Database::requireField()
|
||||
@ -737,14 +741,14 @@ class MSSQLDatabase extends SS_Database {
|
||||
$field['data_type'] .= " default $default";
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'nvarchar':
|
||||
case 'varchar':
|
||||
//Check to see if there's a constraint attached to this column:
|
||||
$clause = $this->getConstraintCheckClause($table, $field['column_name']);
|
||||
if($clause) {
|
||||
$constraints = $this->enumValuesFromCheckClause($clause);
|
||||
$default = substr($field['column_default'], 2, -2);
|
||||
$default=substr($field['column_default'], 2, -2);
|
||||
$field['data_type'] = $this->enum(array(
|
||||
'default' => $default,
|
||||
'name' => $field['column_name'],
|
||||
@ -772,12 +776,24 @@ class MSSQLDatabase extends SS_Database {
|
||||
}
|
||||
}
|
||||
$output[$field['column_name']]=$field;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* This is a stub function. Postgres caches the fieldlist results.
|
||||
*
|
||||
* @param string $tableName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function clear_cached_fieldlist($tableName=false){
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an index on a table.
|
||||
* @param string $tableName The name of the table.
|
||||
@ -787,7 +803,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
public function createIndex($tableName, $indexName, $indexSpec) {
|
||||
$this->query($this->getIndexSqlDefinition($tableName, $indexName, $indexSpec));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This takes the index spec which has been provided by a class (ie static $indexes = blah blah)
|
||||
* and turns it into a proper string.
|
||||
@ -806,10 +822,10 @@ class MSSQLDatabase extends SS_Database {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $indexSpec;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return SQL for dropping and recreating an index
|
||||
*/
|
||||
@ -821,7 +837,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
$indexSpec=trim($indexSpec, '()');
|
||||
$bits=explode(',', $indexSpec);
|
||||
$indexes="\"" . implode("\",\"", $bits) . "\"";
|
||||
|
||||
|
||||
return "$drop CREATE INDEX $index ON \"" . $tableName . "\" (" . $indexes . ");";
|
||||
} else {
|
||||
//create a type-specific index
|
||||
@ -830,7 +846,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
//Enable full text search.
|
||||
$this->createFullTextCatalog();
|
||||
$primary_key = $this->getPrimaryKey($tableName);
|
||||
|
||||
|
||||
$query = '';
|
||||
if($this->fullTextIndexExists($tableName)) {
|
||||
$query .= "\nDROP FULLTEXT INDEX ON \"$tableName\";";
|
||||
@ -839,21 +855,21 @@ class MSSQLDatabase extends SS_Database {
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($indexSpec['type'] == 'unique') {
|
||||
if(!is_array($indexSpec['value'])) $columns = preg_split('/ *, */', trim($indexSpec['value']));
|
||||
else $columns = $indexSpec['value'];
|
||||
$SQL_columnList = '"' . implode('", "', $columns) . '"';
|
||||
|
||||
|
||||
return "$drop CREATE UNIQUE INDEX $index ON \"" . $tableName . "\" ($SQL_columnList);";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getDbSqlDefinition($tableName, $indexName, $indexSpec){
|
||||
return $indexName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Alter an index on a table.
|
||||
* @param string $tableName The name of the table.
|
||||
@ -861,21 +877,21 @@ class MSSQLDatabase extends SS_Database {
|
||||
* @param string $indexSpec The specification of the index, see SS_Database::requireIndex() for more details.
|
||||
*/
|
||||
public function alterIndex($tableName, $indexName, $indexSpec) {
|
||||
$indexSpec = trim($indexSpec);
|
||||
if($indexSpec[0] != '(') {
|
||||
list($indexType, $indexFields) = explode(' ', $indexSpec, 2);
|
||||
} else {
|
||||
$indexFields = $indexSpec;
|
||||
}
|
||||
$indexSpec = trim($indexSpec);
|
||||
if($indexSpec[0] != '(') {
|
||||
list($indexType, $indexFields) = explode(' ',$indexSpec,2);
|
||||
} else {
|
||||
$indexFields = $indexSpec;
|
||||
}
|
||||
|
||||
if(!$indexType) {
|
||||
$indexType = "index";
|
||||
}
|
||||
if(!$indexType) {
|
||||
$indexType = "index";
|
||||
}
|
||||
|
||||
$this->query("DROP INDEX $indexName ON $tableName;");
|
||||
$this->query("DROP INDEX $indexName ON $tableName;");
|
||||
$this->query("ALTER TABLE \"$tableName\" ADD $indexType \"$indexName\" $indexFields");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the list of indexes in a table.
|
||||
* @param string $table The table name.
|
||||
@ -928,14 +944,14 @@ class MSSQLDatabase extends SS_Database {
|
||||
}
|
||||
return $tables;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Empty the given table of all contents.
|
||||
*/
|
||||
public function clearTable($table) {
|
||||
$this->query("TRUNCATE TABLE \"$table\"");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of rows affected by the previous operation.
|
||||
* @return int
|
||||
@ -951,7 +967,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
/**
|
||||
* Return a boolean type-formatted string
|
||||
* We use 'bit' so that we can do numeric-based comparisons
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
@ -959,20 +975,20 @@ class MSSQLDatabase extends SS_Database {
|
||||
$default = ($values['default']) ? '1' : '0';
|
||||
return 'bit not null default ' . $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a date type-formatted string.
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
public function date($values) {
|
||||
return 'datetime null';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a decimal type-formatted string
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
@ -983,18 +999,18 @@ class MSSQLDatabase extends SS_Database {
|
||||
} else {
|
||||
$precision = $values['precision'];
|
||||
}
|
||||
|
||||
|
||||
$defaultValue = '0';
|
||||
if(isset($values['default']) && is_numeric($values['default'])) {
|
||||
$defaultValue = $values['default'];
|
||||
}
|
||||
|
||||
|
||||
return 'decimal(' . $precision . ') not null default ' . $defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a enum type-formatted string
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
@ -1005,31 +1021,31 @@ class MSSQLDatabase extends SS_Database {
|
||||
|
||||
$maxLength = max(array_map('strlen', $values['enums']));
|
||||
|
||||
return "varchar($maxLength) not null default '" . $values['default']
|
||||
. "' check(\"" . $values['name'] . "\" in ('" . implode("','", $values['enums'])
|
||||
return "varchar($maxLength) not null default '" . $values['default']
|
||||
. "' check(\"" . $values['name'] . "\" in ('" . implode("','", $values['enums'])
|
||||
. "'))";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo Make this work like {@link MySQLDatabase::set()}
|
||||
*/
|
||||
public function set($values) {
|
||||
return $this->enum($values);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a float type-formatted string.
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
public function float($values) {
|
||||
return 'float not null default ' . $values['default'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a int type-formatted string
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
@ -1037,21 +1053,21 @@ class MSSQLDatabase extends SS_Database {
|
||||
//We'll be using an 8 digit precision to keep it in line with the serial8 datatype for ID columns
|
||||
return 'numeric(8) not null default ' . (int) $values['default'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a datetime type-formatted string
|
||||
* For MS SQL, we simply return the word 'timestamp', no other parameters are necessary
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
public function ss_datetime($values) {
|
||||
return 'datetime null';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a text type-formatted string
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
@ -1059,20 +1075,20 @@ class MSSQLDatabase extends SS_Database {
|
||||
$collation = self::$collation ? " COLLATE " . self::$collation : "";
|
||||
return "nvarchar(max)$collation null";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a time type-formatted string.
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
public function time($values){
|
||||
return 'time null';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a varchar type-formatted string
|
||||
*
|
||||
*
|
||||
* @params array $values Contains a tokenised list of info about this data type
|
||||
* @return string
|
||||
*/
|
||||
@ -1080,13 +1096,13 @@ class MSSQLDatabase extends SS_Database {
|
||||
$collation = self::$collation ? " COLLATE " . self::$collation : "";
|
||||
return "nvarchar(" . $values['precision'] . ")$collation null";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a 4 digit numeric type.
|
||||
* @return string
|
||||
*/
|
||||
public function year($values) {
|
||||
return 'numeric(4)';
|
||||
return 'numeric(4)';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1104,14 +1120,14 @@ class MSSQLDatabase extends SS_Database {
|
||||
else return 'bigint not null';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the SQL command to get all the tables in this database
|
||||
*/
|
||||
function allTablesSQL(){
|
||||
return "SELECT \"name\" FROM \"sys\".\"tables\";";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this table exists
|
||||
* @todo Make a proper implementation
|
||||
@ -1121,7 +1137,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
$value = DB::query("SELECT table_name FROM information_schema.tables WHERE table_name = '$SQL_tableName'")->value();
|
||||
return (bool)$value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the values of the given enum field
|
||||
* NOTE: Experimental; introduced for db-abstraction and may changed before 2.4 is released.
|
||||
@ -1144,17 +1160,17 @@ class MSSQLDatabase extends SS_Database {
|
||||
function now() {
|
||||
return 'CURRENT_TIMESTAMP';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the database-specific version of the random() function
|
||||
*/
|
||||
function random() {
|
||||
function random(){
|
||||
return 'RAND()';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is a lookup table for data types.
|
||||
*
|
||||
*
|
||||
* For instance, MSSQL uses 'BIGINT', while MySQL uses 'UNSIGNED'
|
||||
* and PostgreSQL uses 'INT'.
|
||||
*/
|
||||
@ -1165,15 +1181,15 @@ class MSSQLDatabase extends SS_Database {
|
||||
if(isset($values[$type])) return $values[$type];
|
||||
else return '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a SQLQuery object into a SQL statement.
|
||||
*/
|
||||
public function sqlQueryToString(SQLQuery $sqlQuery) {
|
||||
if (!$sqlQuery->from) return '';
|
||||
|
||||
|
||||
if($sqlQuery->orderby && strtoupper(trim($sqlQuery->orderby)) == 'RAND()') $sqlQuery->orderby = "NEWID()";
|
||||
|
||||
|
||||
//Get the limit and offset
|
||||
$limit='';
|
||||
$offset='0';
|
||||
@ -1181,7 +1197,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
$limit=$sqlQuery->limit['limit'];
|
||||
if(isset($sqlQuery->limit['start']))
|
||||
$offset=$sqlQuery->limit['start'];
|
||||
|
||||
|
||||
} else if(preg_match('/^([0-9]+) offset ([0-9]+)$/i', trim($sqlQuery->limit), $matches)) {
|
||||
$limit = $matches[1];
|
||||
$offset = $matches[2];
|
||||
@ -1194,7 +1210,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
$limit = $bits[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$text = '';
|
||||
$suffixText = '';
|
||||
$nestedQuery = false;
|
||||
@ -1202,16 +1218,16 @@ class MSSQLDatabase extends SS_Database {
|
||||
// DELETE queries
|
||||
if($sqlQuery->delete) {
|
||||
$text = 'DELETE ';
|
||||
|
||||
|
||||
// SELECT queries
|
||||
} else {
|
||||
$distinct = $sqlQuery->distinct ? "DISTINCT " : "";
|
||||
|
||||
|
||||
// If there's a limit but no offset, just use 'TOP X'
|
||||
// rather than the more complex sub-select method
|
||||
if ($limit != 0 && $offset == 0) {
|
||||
$text = "SELECT $distinct TOP $limit";
|
||||
|
||||
|
||||
// If there's a limit and an offset, then we need to do a subselect
|
||||
} else if($limit && $offset) {
|
||||
if($sqlQuery->orderby) {
|
||||
@ -1229,7 +1245,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
} else {
|
||||
$text = "SELECT $distinct";
|
||||
}
|
||||
|
||||
|
||||
// Now add the columns to be selected
|
||||
$text .= implode(", ", $sqlQuery->select);
|
||||
}
|
||||
@ -1239,10 +1255,10 @@ class MSSQLDatabase extends SS_Database {
|
||||
if($sqlQuery->groupby) $text .= " GROUP BY " . implode(", ", $sqlQuery->groupby);
|
||||
if($sqlQuery->having) $text .= " HAVING ( " . implode(" ) AND ( ", $sqlQuery->having) . " )";
|
||||
if(!$nestedQuery && $sqlQuery->orderby) $text .= " ORDER BY " . $sqlQuery->orderby;
|
||||
|
||||
|
||||
// $suffixText is used by the nested queries to create an offset limit
|
||||
if($suffixText) $text .= $suffixText;
|
||||
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
@ -1251,10 +1267,11 @@ class MSSQLDatabase extends SS_Database {
|
||||
* @param string $value String to escape
|
||||
* @return string Escaped string
|
||||
*/
|
||||
function addslashes($value) {
|
||||
$value = str_replace("'", "''", $value);
|
||||
$value = str_replace("\0", "[NULL]", $value);
|
||||
return $value;
|
||||
function addslashes($value){
|
||||
$value=str_replace("'","''",$value);
|
||||
$value=str_replace("\0","[NULL]",$value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1269,7 +1286,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
* The core search engine configuration.
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* @param array classesToSearch computes all descendants and includes them. Check is done via WHERE clause.
|
||||
* @param string $keywords Keywords as a space separated string
|
||||
* @return object DataObjectSet of result pages
|
||||
@ -1284,7 +1301,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
$allClassesToSearch = array_merge($allClassesToSearch, ClassInfo::dataClassesFor($class));
|
||||
}
|
||||
$allClassesToSearch = array_unique($allClassesToSearch);
|
||||
|
||||
|
||||
//Get a list of all the tables and columns we'll be searching on:
|
||||
$fulltextColumns = DB::query('EXEC sp_help_fulltext_columns');
|
||||
$queries = array();
|
||||
@ -1406,7 +1423,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
else {
|
||||
$keywords = Convert::raw2sql(str_replace(array('&','|','!','"','\''), '', trim($keywords)));
|
||||
}
|
||||
|
||||
|
||||
// Remove stopwords, concat with ANDs
|
||||
$keywords = explode(' ', $keywords);
|
||||
$keywords = self::removeStopwords($keywords);
|
||||
@ -1419,11 +1436,11 @@ class MSSQLDatabase extends SS_Database {
|
||||
|
||||
return "FREETEXTTABLE(\"$tableName\", ($fieldNames), '$keywords')";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove stopwords that would kill a MSSQL full-text query
|
||||
*
|
||||
* @param array $keywords
|
||||
* @param array $keywords
|
||||
*
|
||||
* @return array $keywords with stopwords removed
|
||||
*/
|
||||
@ -1435,14 +1452,14 @@ class MSSQLDatabase extends SS_Database {
|
||||
}
|
||||
return $goodKeywords;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does this database support transactions?
|
||||
*/
|
||||
public function supportsTransactions(){
|
||||
return $this->supportsTransactions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is a quick lookup to discover if the database supports particular extensions
|
||||
* Currently, MSSQL supports no extensions
|
||||
@ -1457,7 +1474,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start transaction. READ ONLY not supported.
|
||||
*/
|
||||
@ -1469,14 +1486,14 @@ class MSSQLDatabase extends SS_Database {
|
||||
if (!$result) $this->databaseError("Couldn't start the transaction.", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a savepoint that you can jump back to if you encounter problems
|
||||
*/
|
||||
public function transactionSavepoint($savepoint){
|
||||
DB::query("SAVE TRANSACTION \"$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
|
||||
@ -1494,7 +1511,7 @@ class MSSQLDatabase extends SS_Database {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commit everything inside this transaction so far
|
||||
*/
|
||||
@ -1565,9 +1582,9 @@ class MSSQLDatabase extends SS_Database {
|
||||
}
|
||||
|
||||
return '(' . implode(' + ', $strings) . ')';
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to return an SQL datetime expression for MSSQL.
|
||||
* used for querying a datetime addition
|
||||
@ -1672,13 +1689,13 @@ class MSSQLQuery extends SS_Query {
|
||||
|
||||
public function __destruct() {
|
||||
if(is_resource($this->handle)) {
|
||||
if($this->mssql) {
|
||||
mssql_free_result($this->handle);
|
||||
} else {
|
||||
sqlsrv_free_stmt($this->handle);
|
||||
}
|
||||
if($this->mssql) {
|
||||
mssql_free_result($this->handle);
|
||||
} else {
|
||||
sqlsrv_free_stmt($this->handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function seek($row) {
|
||||
if(!is_resource($this->handle)) return false;
|
||||
@ -1692,7 +1709,6 @@ class MSSQLQuery extends SS_Query {
|
||||
|
||||
public function numRecords() {
|
||||
if(!is_resource($this->handle)) return false;
|
||||
|
||||
if($this->mssql) {
|
||||
return mssql_num_rows($this->handle);
|
||||
} else {
|
||||
@ -1706,12 +1722,13 @@ class MSSQLQuery extends SS_Query {
|
||||
}
|
||||
|
||||
public function nextRecord() {
|
||||
|
||||
if(!is_resource($this->handle)) return false;
|
||||
|
||||
// Coalesce rather than replace common fields.
|
||||
$output = array();
|
||||
|
||||
if($this->mssql) {
|
||||
if($this->mssql) {
|
||||
if($data = mssql_fetch_row($this->handle)) {
|
||||
foreach($data as $columnIdx => $value) {
|
||||
$columnName = mssql_field_name($this->handle, $columnIdx);
|
||||
@ -1733,7 +1750,7 @@ class MSSQLQuery extends SS_Query {
|
||||
foreach($fields as $columnIdx => $field) {
|
||||
$value = $data[$columnIdx];
|
||||
if($value instanceof DateTime) $value = $value->format('Y-m-d H:i:s');
|
||||
|
||||
|
||||
// $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[$field['Name']])) {
|
||||
|
Loading…
Reference in New Issue
Block a user