diff --git a/code/MSSQLDatabase.php b/code/MSSQLDatabase.php index c5a974b..8a1c04d 100644 --- a/code/MSSQLDatabase.php +++ b/code/MSSQLDatabase.php @@ -1,69 +1,69 @@ Connecting using Windows - * + * * 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/ - * + * *

Connecting using Linux or Mac OS X

- * + * * The following commands assume you used the default package manager * to install PHP with the operating system. - * + * * Debian, and Ubuntu: * apt-get install php5-sybase * * Fedora, CentOS and RedHat: * yum install php-mssql - * + * * Mac OS X (MacPorts): * port install php5-mssql - * + * * 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']])) {