From 30fd9fff145c9126c0d01cc2566b181aa9f4782c Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 26 Apr 2011 09:17:51 -0400 Subject: [PATCH 01/32] Changed the import files and Mssql class name to the new structure. --- lib/Cake/Model/Datasource/Database/Mssql.php | 4 +++- .../Model/Datasource/Database/MssqlTest.php | 18 ++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index b09e58a77..11e2f6caa 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -17,6 +17,8 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +App::uses('DboSource', 'Model/Datasource'); + /** * MS SQL layer for DBO * @@ -24,7 +26,7 @@ * * @package cake.libs.model.datasources.dbo */ -class DboMssql extends DboSource { +class Mssql extends DboSource { /** * Driver description diff --git a/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php b/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php index d4d1de10e..bc7d852c0 100644 --- a/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php +++ b/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php @@ -1,6 +1,6 @@ db = new DboMssqlTestDb($db->config); + $this->db = new MssqlTestDb($db->config); $this->model = new MssqlTestModel(); } From 4e8b8639100c67b8c89edafc7eef42aa4f4afc91 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 27 Apr 2011 20:41:29 -0400 Subject: [PATCH 02/32] Initial changes for init the connection with MSSQL. --- lib/Cake/Model/Datasource/Database/Mssql.php | 78 ++++++-------------- 1 file changed, 21 insertions(+), 57 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 11e2f6caa..77213e3e7 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -64,11 +64,10 @@ class Mssql extends DboSource { */ protected $_baseConfig = array( 'persistent' => true, - 'host' => 'localhost', - 'login' => 'root', + 'host' => '(local)\sqlexpress', + 'login' => '', 'password' => '', - 'database' => 'cake', - 'port' => '1433', + 'database' => 'cake' ); /** @@ -109,22 +108,6 @@ class Mssql extends DboSource { * @access private */ private $__lastQueryHadError = false; -/** - * MS SQL DBO driver constructor; sets SQL Server error reporting defaults - * - * @param array $config Configuration data from app/config/databases.php - * @return boolean True if connected successfully, false on error - */ - function __construct($config, $autoConnect = true) { - if ($autoConnect) { - if (!function_exists('mssql_min_message_severity')) { - trigger_error(__d('cake_dev', "PHP SQL Server interface is not installed, cannot continue. For troubleshooting information, see http://php.net/mssql/"), E_USER_WARNING); - } - mssql_min_message_severity(15); - mssql_min_error_severity(2); - } - return parent::__construct($config, $autoConnect); - } /** * Connects to the database using options in the given configuration array. @@ -133,53 +116,34 @@ class Mssql extends DboSource { */ function connect() { $config = $this->config; - - $os = env('OS'); - if (!empty($os) && strpos($os, 'Windows') !== false) { - $sep = ','; - } else { - $sep = ':'; - } $this->connected = false; - - if (is_numeric($config['port'])) { - $port = $sep . $config['port']; // Port number - } elseif ($config['port'] === null) { - $port = ''; // No port - SQL Server 2005 - } else { - $port = '\\' . $config['port']; // Named pipe - } - - if (!$config['persistent']) { - $this->connection = mssql_connect($config['host'] . $port, $config['login'], $config['password'], true); - } else { - $this->connection = mssql_pconnect($config['host'] . $port, $config['login'], $config['password']); - } - - if (mssql_select_db($config['database'], $this->connection)) { - $this->_execute("SET DATEFORMAT ymd"); + try { + $flags = array(PDO::ATTR_PERSISTENT => $config['persistent']); + if (!empty($config['encoding'])) { + $flags[PDO::SQLSRV_ATTR_ENCODING] = $config['encoding']; + } + $this->_connection = new PDO( + "sqlsrv:server={$config['host']};Database={$config['database']}", + $config['login'], + $config['password'], + $flags + ); $this->connected = true; + } catch (PDOException $e) { + throw new MissingConnectionException(array('class' => $e->getMessage())); } + +// $this->_execute("SET DATEFORMAT ymd"); return $this->connected; } /** - * Check that MsSQL is installed/loaded + * Check that PDO SQL Server is installed/loaded * * @return boolean */ - function enabled() { - return extension_loaded('mssql'); - } -/** - * Disconnects from database. - * - * @return boolean True if the database could be disconnected, else false - */ - function disconnect() { - @mssql_free_result($this->results); - $this->connected = !@mssql_close($this->connection); - return !$this->connected; + public function enabled() { + return in_array('sqlsrv', PDO::getAvailableDrivers()); } /** From 244bc1369a69d7c311ae9dd89ccba8e35521c68f Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 27 Apr 2011 20:42:47 -0400 Subject: [PATCH 03/32] Removed the execute method from MSSQL. It is provided by DboDatasource now. --- lib/Cake/Model/Datasource/Database/Mssql.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 77213e3e7..603b22b57 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -146,18 +146,6 @@ class Mssql extends DboSource { return in_array('sqlsrv', PDO::getAvailableDrivers()); } -/** - * Executes given SQL statement. - * - * @param string $sql SQL statement - * @return resource Result resource identifier - */ - protected function _execute($sql) { - $result = @mssql_query($sql, $this->connection); - $this->__lastQueryHadError = ($result === false); - return $result; - } - /** * Returns an array of sources (tables) in the database. * From 3f984b68facd9ba3c1eaa49d87d2bd115a87ce61 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 27 Apr 2011 20:53:42 -0400 Subject: [PATCH 04/32] Update the listSources. --- lib/Cake/Model/Datasource/Database/Mssql.php | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 603b22b57..173ca8994 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -151,26 +151,26 @@ class Mssql extends DboSource { * * @return array Array of tablenames in the database */ - function listSources() { + public function listSources() { $cache = parent::listSources(); - - if ($cache != null) { + if ($cache !== null) { return $cache; } - $result = $this->fetchAll('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES', false); + $result = $this->_execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'")); - if (!$result || empty($result)) { + if (!$result) { + $result->closeCursor(); return array(); - } else { - $tables = array(); - - foreach ($result as $table) { - $tables[] = $table[0]['TABLE_NAME']; - } - - parent::listSources($tables); - return $tables; } + + $tables = array(); + while ($line = $result->fetch(PDO::FETCH_ASSOC)) { + $tables[] = $line['TABLE_NAME']; + } + + $result->closeCursor(); + parent::listSources($tables); + return $tables; } /** From 37d26f29234978c59efcd11189aa6e1c1462e502 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 27 Apr 2011 21:27:53 -0400 Subject: [PATCH 05/32] Update the describe. --- lib/Cake/Model/Datasource/Database/Mssql.php | 31 +++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 173ca8994..0f2fa9d05 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -181,40 +181,43 @@ class Mssql extends DboSource { */ function describe($model) { $cache = parent::describe($model); - if ($cache != null) { return $cache; } - - $table = $this->fullTableName($model, false); - $cols = $this->fetchAll("SELECT COLUMN_NAME as Field, DATA_TYPE as Type, COL_LENGTH('" . $table . "', COLUMN_NAME) as Length, IS_NULLABLE As [Null], COLUMN_DEFAULT as [Default], COLUMNPROPERTY(OBJECT_ID('" . $table . "'), COLUMN_NAME, 'IsIdentity') as [Key], NUMERIC_SCALE as Size FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" . $table . "'", false); - $fields = false; + $table = $this->fullTableName($model, false); + $cols = $this->_execute("SELECT COLUMN_NAME as Field, DATA_TYPE as Type, COL_LENGTH('" . $table . "', COLUMN_NAME) as Length, IS_NULLABLE As [Null], COLUMN_DEFAULT as [Default], COLUMNPROPERTY(OBJECT_ID('" . $table . "'), COLUMN_NAME, 'IsIdentity') as [Key], NUMERIC_SCALE as Size FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" . $table . "'"); + if (!$cols) { + throw new CakeException(__d('cake_dev', 'Could not describe table for %s', $model->name)); + } + foreach ($cols as $column) { - $field = $column[0]['Field']; + $field = $column->Field; $fields[$field] = array( - 'type' => $this->column($column[0]['Type']), - 'null' => (strtoupper($column[0]['Null']) == 'YES'), - 'default' => preg_replace("/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", "$1", $column[0]['Default']), - 'length' => intval($column[0]['Length']), - 'key' => ($column[0]['Key'] == '1') ? 'primary' : false + 'type' => $this->column($column->Type), + 'null' => ($column->Null === 'YES' ? true : false), + 'default' => preg_replace("/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", "$1", $column->Default), + 'length' => intval($column->Type), + 'key' => ($column->Key == '1') ? 'primary' : false ); + if ($fields[$field]['default'] === 'null') { $fields[$field]['default'] = null; } else { $this->value($fields[$field]['default'], $fields[$field]['type']); } - if ($fields[$field]['key'] && $fields[$field]['type'] == 'integer') { + if ($fields[$field]['key'] !== false && $fields[$field]['type'] == 'integer') { $fields[$field]['length'] = 11; - } elseif (!$fields[$field]['key']) { + } elseif ($fields[$field]['key'] === false) { unset($fields[$field]['key']); } if (in_array($fields[$field]['type'], array('date', 'time', 'datetime', 'timestamp'))) { $fields[$field]['length'] = null; } } - $this->__cacheDescription($this->fullTableName($model, false), $fields); + $this->__cacheDescription($table, $fields); + $cols->closeCursor(); return $fields; } From f11b153d80c284853d18df6b1946e600f91f29dc Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 27 Apr 2011 23:10:44 -0400 Subject: [PATCH 06/32] Removed the value method from MSSQL. It is provided by DboDatasource now. --- lib/Cake/Model/Datasource/Database/Mssql.php | 43 -------------------- 1 file changed, 43 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 0f2fa9d05..0d5b4fc93 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -221,49 +221,6 @@ class Mssql extends DboSource { return $fields; } -/** - * Returns a quoted and escaped string of $data for use in an SQL statement. - * - * @param string $data String to be prepared for use in an SQL statement - * @param string $column The column into which this data will be inserted - * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided - * @return string Quoted and escaped data - */ - function value($data, $column = null, $safe = false) { - $parent = parent::value($data, $column, $safe); - - if ($parent != null) { - return $parent; - } - if ($data === null) { - return 'NULL'; - } - if (in_array($column, array('integer', 'float', 'binary')) && $data === '') { - return 'NULL'; - } - if ($data === '') { - return "''"; - } - - switch ($column) { - case 'boolean': - $data = $this->boolean((bool)$data); - break; - default: - if (get_magic_quotes_gpc()) { - $data = stripslashes(str_replace("'", "''", $data)); - } else { - $data = str_replace("'", "''", $data); - } - break; - } - - if (in_array($column, array('integer', 'float', 'binary')) && is_numeric($data)) { - return $data; - } - return "'" . $data . "'"; - } - /** * Generates the fields list of an SQL query. * From 83b81ffd87d3f8efc0ba1dff783f38caf4b56cb9 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Thu, 28 Apr 2011 21:37:34 -0400 Subject: [PATCH 07/32] Removed more methods that is implemented by DboSource. --- lib/Cake/Model/Datasource/Database/Mssql.php | 52 -------------------- 1 file changed, 52 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 0d5b4fc93..7bcefc203 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -338,58 +338,6 @@ class Mssql extends DboSource { return parent::update($model, array_keys($fields), array_values($fields), $conditions); } -/** - * Returns a formatted error message from previous database operation. - * - * @return string Error message with error number - */ - function lastError() { - if ($this->__lastQueryHadError) { - $error = mssql_get_last_message(); - if ($error && !preg_match('/contexto de la base de datos a|contesto di database|changed database|contexte de la base de don|datenbankkontext/i', $error)) { - return $error; - } - } - return null; - } - -/** - * Returns number of affected rows in previous database operation. If no previous operation exists, - * this returns false. - * - * @return integer Number of affected rows - */ - function lastAffected() { - if ($this->_result) { - return mssql_rows_affected($this->connection); - } - return null; - } - -/** - * Returns number of rows in previous resultset. If no previous resultset exists, - * this returns false. - * - * @return integer Number of rows in resultset - */ - function lastNumRows() { - if ($this->_result) { - return @mssql_num_rows($this->_result); - } - return null; - } - -/** - * Returns the ID generated from the previous INSERT operation. - * - * @param unknown_type $source - * @return in - */ - function lastInsertId($source = null) { - $id = $this->fetchRow('SELECT SCOPE_IDENTITY() AS insertID', false); - return $id[0]['insertID']; - } - /** * Returns a limit statement in the correct format for the particular database. * From a7a86e42a310bfe4371eb7229965aaf9c79fbb7e Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Thu, 28 Apr 2011 21:46:56 -0400 Subject: [PATCH 08/32] Changes for fetch results. --- lib/Cake/Model/Datasource/Database/Mssql.php | 33 ++++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 7bcefc203..2eb8b6c21 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -406,19 +406,18 @@ class Mssql extends DboSource { } /** - * Enter description here... + * Builds a map of the columns contained in a result * - * @param unknown_type $results + * @param PDOStatement $results */ - function resultSet(&$results) { - $this->results =& $results; + function resultSet($results) { $this->map = array(); - $numFields = mssql_num_fields($results); + $numFields = $results->columnCount(); $index = 0; $j = 0; - while ($j < $numFields) { - $column = mssql_field_name($results, $j); + while ($numFields-- > 0) { + $column = $results->getColumnMeta($index); if (strpos($column, '__')) { if (isset($this->__fieldMappings[$column]) && strpos($this->__fieldMappings[$column], '.')) { @@ -536,22 +535,22 @@ class Mssql extends DboSource { /** * Fetches the next row from the current result set * - * @return unknown + * @return mixed */ function fetchResult() { - if ($row = mssql_fetch_row($this->results)) { + if ($row = $this->_result->fetch()) { $resultRow = array(); - $i = 0; - - foreach ($row as $index => $field) { - list($table, $column) = $this->map[$index]; - $resultRow[$table][$column] = $row[$index]; - $i++; + foreach ($this->map as $col => $meta) { + list($table, $column, $type) = $meta; + $resultRow[$table][$column] = $row[$col]; + if ($type === 'boolean' && !is_null($row[$col])) { + $resultRow[$table][$column] = $this->boolean($resultRow[$table][$column]); + } } return $resultRow; - } else { - return false; } + $this->_result->closeCursor(); + return false; } /** From b91f6eb991758957a66750b753dc467b4defd15d Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Thu, 28 Apr 2011 21:52:22 -0400 Subject: [PATCH 09/32] Fixed error in change. --- lib/Cake/Model/Datasource/Database/Mssql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 2eb8b6c21..c6d3db238 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -156,7 +156,7 @@ class Mssql extends DboSource { if ($cache !== null) { return $cache; } - $result = $this->_execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'")); + $result = $this->_execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'"); if (!$result) { $result->closeCursor(); From 24bf56b44e056cc525585ead4b920d4adb7a8902 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Thu, 28 Apr 2011 22:02:54 -0400 Subject: [PATCH 10/32] Revert "Removed the value method from MSSQL. It is provided by DboDatasource now." This reverts commit bf0fb8302385d384239939df2ea6bc6f1a8dbaa0. --- lib/Cake/Model/Datasource/Database/Mssql.php | 43 ++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index c6d3db238..940344845 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -221,6 +221,49 @@ class Mssql extends DboSource { return $fields; } +/** + * Returns a quoted and escaped string of $data for use in an SQL statement. + * + * @param string $data String to be prepared for use in an SQL statement + * @param string $column The column into which this data will be inserted + * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided + * @return string Quoted and escaped data + */ + function value($data, $column = null, $safe = false) { + $parent = parent::value($data, $column, $safe); + + if ($parent != null) { + return $parent; + } + if ($data === null) { + return 'NULL'; + } + if (in_array($column, array('integer', 'float', 'binary')) && $data === '') { + return 'NULL'; + } + if ($data === '') { + return "''"; + } + + switch ($column) { + case 'boolean': + $data = $this->boolean((bool)$data); + break; + default: + if (get_magic_quotes_gpc()) { + $data = stripslashes(str_replace("'", "''", $data)); + } else { + $data = str_replace("'", "''", $data); + } + break; + } + + if (in_array($column, array('integer', 'float', 'binary')) && is_numeric($data)) { + return $data; + } + return "'" . $data . "'"; + } + /** * Generates the fields list of an SQL query. * From 3231755d63aec0ce841ed24c4eb158b5415b70ce Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Thu, 28 Apr 2011 22:12:07 -0400 Subject: [PATCH 11/32] Fixed the tests for value. --- lib/Cake/Model/Datasource/Database/Mssql.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 940344845..f6de416f2 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -232,6 +232,12 @@ class Mssql extends DboSource { function value($data, $column = null, $safe = false) { $parent = parent::value($data, $column, $safe); + if ($column === 'float' && strpos($data, '.') !== false) { + return rtrim($data, '0'); + } + if ($parent === "''") { + return 'NULL'; + } if ($parent != null) { return $parent; } From 9dfc7d65939001392a7aa4f70aa1980ad14a452f Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Thu, 28 Apr 2011 22:20:11 -0400 Subject: [PATCH 12/32] Changed the field map to protected to run the tests. It is not requeried to be private. --- lib/Cake/Model/Datasource/Database/Mssql.php | 22 +++++++++---------- .../Model/Datasource/Database/MssqlTest.php | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index f6de416f2..1383ca138 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -55,7 +55,7 @@ class Mssql extends DboSource { * * @var array */ - private $__fieldMappings = array(); + protected $_fieldMappings = array(); /** * Base configuration settings for MS SQL driver @@ -294,7 +294,7 @@ class Mssql extends DboSource { $prepend = 'DISTINCT '; $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i])); } - $fieldAlias = count($this->__fieldMappings); + $fieldAlias = count($this->_fieldMappings); if (!preg_match('/\s+AS\s+/i', $fields[$i])) { if (substr($fields[$i], -1) == '*') { @@ -311,12 +311,12 @@ class Mssql extends DboSource { } if (strpos($fields[$i], '.') === false) { - $this->__fieldMappings[$alias . '__' . $fieldAlias] = $alias . '.' . $fields[$i]; + $this->_fieldMappings[$alias . '__' . $fieldAlias] = $alias . '.' . $fields[$i]; $fieldName = $this->name($alias . '.' . $fields[$i]); $fieldAlias = $this->name($alias . '__' . $fieldAlias); } else { $build = explode('.', $fields[$i]); - $this->__fieldMappings[$build[0] . '__' . $fieldAlias] = $fields[$i]; + $this->_fieldMappings[$build[0] . '__' . $fieldAlias] = $fields[$i]; $fieldName = $this->name($build[0] . '.' . $build[1]); $fieldAlias = $this->name(preg_replace("/^\[(.+)\]$/", "$1", $build[0]) . '__' . $fieldAlias); } @@ -469,10 +469,10 @@ class Mssql extends DboSource { $column = $results->getColumnMeta($index); if (strpos($column, '__')) { - if (isset($this->__fieldMappings[$column]) && strpos($this->__fieldMappings[$column], '.')) { - $map = explode('.', $this->__fieldMappings[$column]); - } elseif (isset($this->__fieldMappings[$column])) { - $map = array(0, $this->__fieldMappings[$column]); + if (isset($this->_fieldMappings[$column]) && strpos($this->_fieldMappings[$column], '.')) { + $map = explode('.', $this->_fieldMappings[$column]); + } elseif (isset($this->_fieldMappings[$column])) { + $map = array(0, $this->_fieldMappings[$column]); } else { $map = array(0, $column); } @@ -557,10 +557,10 @@ class Mssql extends DboSource { * @access private */ function __mapFields($sql) { - if (empty($sql) || empty($this->__fieldMappings)) { + if (empty($sql) || empty($this->_fieldMappings)) { return $sql; } - foreach ($this->__fieldMappings as $key => $val) { + foreach ($this->_fieldMappings as $key => $val) { $sql = preg_replace('/' . preg_quote($val) . '/', $this->name($key), $sql); $sql = preg_replace('/' . preg_quote($this->name($val)) . '/', $this->name($key), $sql); } @@ -577,7 +577,7 @@ class Mssql extends DboSource { */ function read($model, $queryData = array(), $recursive = null) { $results = parent::read($model, $queryData, $recursive); - $this->__fieldMappings = array(); + $this->_fieldMappings = array(); return $results; } diff --git a/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php b/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php index bc7d852c0..f02242df5 100644 --- a/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php +++ b/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php @@ -119,7 +119,7 @@ class MssqlTestDb extends Mssql { * @return void */ function clearFieldMappings() { - $this->__fieldMappings = array(); + $this->_fieldMappings = array(); } } From 8b78507b3db17333e13501130b12a0a6b02bbb9b Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Fri, 29 Apr 2011 09:14:09 -0400 Subject: [PATCH 13/32] Changed the test setup to not run the test if test db config is not mssql. --- .../Case/Model/Datasource/Database/MssqlTest.php | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php b/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php index f02242df5..eead8d3c4 100644 --- a/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php +++ b/lib/Cake/tests/Case/Model/Datasource/Database/MssqlTest.php @@ -287,14 +287,6 @@ class MssqlTest extends CakeTestCase { * @access public */ public $fixtures = array('core.category'); -/** - * Skip if cannot connect to mssql - * - */ - public function skip() { - $this->_initDb(); - $this->skipUnless($this->db->config['driver'] == 'mssql', '%s SQL Server connection not available'); - } /** * Make sure all fixtures tables are being created @@ -319,8 +311,11 @@ class MssqlTest extends CakeTestCase { * */ public function setUp() { - $db = ConnectionManager::getDataSource('test'); - $this->db = new MssqlTestDb($db->config); + $this->Dbo = ConnectionManager::getDataSource('test'); + if (!($this->Dbo instanceof Mssql)) { + $this->markTestSkipped('Please configure the test datasource to use SQL Server.'); + } + $this->db = new MssqlTestDb($this->Dbo->config); $this->model = new MssqlTestModel(); } From 37b9bd59bc0f1154ac5e8e839977d246e73cccf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renan=20Gon=C3=A7alves?= Date: Wed, 18 May 2011 20:12:36 +0200 Subject: [PATCH 14/32] Trying to fix the Mssql as much as possible. Adding parameter to the insertMulti() method because Mssql tries to get the table schema and it fails most of the time. --- lib/Cake/Model/Datasource/Database/Mssql.php | 58 +++---- lib/Cake/Model/Datasource/DboSource.php | 3 +- lib/Cake/Model/Model.php | 2 +- .../Model/Datasource/Database/MssqlTest.php | 155 ++++++------------ lib/Cake/Test/Case/Model/ModelReadTest.php | 25 ++- .../TestSuite/Fixture/CakeTestFixture.php | 2 +- 6 files changed, 102 insertions(+), 143 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 1383ca138..9dd4adcb4 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -161,16 +161,17 @@ class Mssql extends DboSource { if (!$result) { $result->closeCursor(); return array(); - } + } else { + $tables = array(); - $tables = array(); - while ($line = $result->fetch(PDO::FETCH_ASSOC)) { - $tables[] = $line['TABLE_NAME']; - } + while ($line = $result->fetch()) { + $tables[] = $line[0]; + } - $result->closeCursor(); - parent::listSources($tables); - return $tables; + $result->closeCursor(); + parent::listSources($tables); + return $tables; + } } /** @@ -197,7 +198,7 @@ class Mssql extends DboSource { 'type' => $this->column($column->Type), 'null' => ($column->Null === 'YES' ? true : false), 'default' => preg_replace("/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", "$1", $column->Default), - 'length' => intval($column->Type), + 'length' => intval($column->Length), 'key' => ($column->Key == '1') ? 'primary' : false ); @@ -348,7 +349,7 @@ class Mssql extends DboSource { if (!empty($values)) { $fields = array_combine($fields, $values); } - $primaryKey = $this->_getPrimaryKey($model); + $primaryKey = $this->_getPrimaryKey($model->schema()); if (array_key_exists($primaryKey, $fields)) { if (empty($fields[$primaryKey])) { @@ -463,24 +464,24 @@ class Mssql extends DboSource { $this->map = array(); $numFields = $results->columnCount(); $index = 0; - $j = 0; while ($numFields-- > 0) { $column = $results->getColumnMeta($index); + $name = $column['name']; - if (strpos($column, '__')) { - if (isset($this->_fieldMappings[$column]) && strpos($this->_fieldMappings[$column], '.')) { - $map = explode('.', $this->_fieldMappings[$column]); - } elseif (isset($this->_fieldMappings[$column])) { - $map = array(0, $this->_fieldMappings[$column]); + if (strpos($name, '__')) { + if (isset($this->_fieldMappings[$name]) && strpos($this->_fieldMappings[$name], '.')) { + $map = explode('.', $this->_fieldMappings[$name]); + } elseif (isset($this->_fieldMappings[$name])) { + $map = array(0, $this->_fieldMappings[$name]); } else { - $map = array(0, $column); + $map = array(0, $name); } - $this->map[$index++] = $map; } else { - $this->map[$index++] = array(0, $column); + $map = array(0, $name); } - $j++; + $map[] = ($column['sqlsrv:decl_type'] == 'bit') ? 'boolean' : $column['native_type']; + $this->map[$index++] = $map; } } @@ -608,9 +609,10 @@ class Mssql extends DboSource { * @param string $table * @param string $fields * @param array $values + * @param array $schema */ - public function insertMulti($table, $fields, $values) { - $primaryKey = $this->_getPrimaryKey($table); + public function insertMulti($table, $fields, $values, $schema) { + $primaryKey = $this->_getPrimaryKey($schema); $hasPrimaryKey = $primaryKey != null && ( (is_array($fields) && in_array($primaryKey, $fields) || (is_string($fields) && strpos($fields, $this->startQuote . $primaryKey . $this->endQuote) !== false)) @@ -619,7 +621,7 @@ class Mssql extends DboSource { if ($hasPrimaryKey) { $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON'); } - parent::insertMulti($table, $fields, $values); + parent::insertMulti($table, $fields, $values, $schema); if ($hasPrimaryKey) { $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF'); } @@ -673,17 +675,11 @@ class Mssql extends DboSource { /** * Makes sure it will return the primary key * - * @param mixed $model + * @param array $schema * @access protected * @return string */ - function _getPrimaryKey($model) { - if (is_object($model)) { - $schema = $model->schema(); - } else { - $schema = $this->describe($model); - } - + function _getPrimaryKey($schema) { foreach ($schema as $field => $props) { if (isset($props['key']) && $props['key'] == 'primary') { return $field; diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 8d3db098b..f796b1e3e 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -2796,8 +2796,9 @@ class DboSource extends DataSource { * @param string $table * @param string $fields * @param array $values + * @param array $schema */ - public function insertMulti($table, $fields, $values) { + public function insertMulti($table, $fields, $values, $schema) { $table = $this->fullTableName($table); $holder = implode(',', array_fill(0, count($fields), '?')); $fields = implode(', ', array_map(array(&$this, 'name'), $fields)); diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 63f1f6017..acf7bdd3f 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -1522,7 +1522,7 @@ class Model extends Object { } if (!empty($newValues)) { - $db->insertMulti($this->{$join}, $fields, $newValues); + $db->insertMulti($this->{$join}, $fields, $newValues, $this->{$join}->schema()); } } } diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php index dcfe2e8ad..63e14b261 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php @@ -36,19 +36,20 @@ class MssqlTestDb extends Mssql { public $simulated = array(); /** - * simalate property + * simulate property * * @var array * @access public */ - public $simulate = true; + public $simulate = false; + /** - * fetchAllResultsStack + * execute results stack * * @var array * @access public */ - public $fetchAllResultsStack = array(); + public $executeResultsStack = array(); /** * execute method @@ -60,7 +61,7 @@ class MssqlTestDb extends Mssql { function _execute($sql) { if ($this->simulate) { $this->simulated[] = $sql; - return null; + return empty($this->executeResultsStack) ? null : array_pop($this->executeResultsStack); } else { return parent::_execute($sql); } @@ -77,21 +78,6 @@ class MssqlTestDb extends Mssql { return $this->conditions(array('id' => array(1, 2))); } -/** - * fetchAll method - * - * @param mixed $sql - * @access protected - * @return void - */ - function fetchAll($sql, $cache = true, $modelName = null) { - $result = parent::fetchAll($sql, $cache, $modelName); - if (!empty($this->fetchAllResultsStack)) { - return array_pop($this->fetchAllResultsStack); - } - return $result; - } - /** * getLastQuery method * @@ -105,12 +91,12 @@ class MssqlTestDb extends Mssql { /** * getPrimaryKey method * - * @param mixed $model + * @param array $schema * @access public * @return void */ - function getPrimaryKey($model) { - return parent::_getPrimaryKey($model); + function getPrimaryKey($schema) { + return parent::_getPrimaryKey($schema); } /** * clearFieldMappings method @@ -211,17 +197,6 @@ class MssqlTestModel extends Model { function findAll($conditions = null, $fields = null, $order = null, $recursive = null) { return $conditions; } - -/** - * setSchema method - * - * @param array $schema - * @access public - * @return void - */ - function setSchema($schema) { - $this->_schema = $schema; - } } /** @@ -258,6 +233,21 @@ class MssqlClientTestModel extends Model { 'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null) ); } + +/** + * MssqlTestResultIterator class + * + * @package cake.tests.cases.libs.model.datasources + */ +class MssqlTestResultIterator extends ArrayIterator { +/** + * closeCursor method + * + * @access public + */ + public function closeCursor() {} +} + /** * MssqlTest class * @@ -292,19 +282,15 @@ class MssqlTest extends CakeTestCase { * Make sure all fixtures tables are being created * */ - public function start() { - $this->db->simulate = false; - parent::start(); + public function startTest($method) { $this->db->simulate = true; } /** * Make sure all fixtures tables are being dropped * */ - public function end() { + public function endTest($method) { $this->db->simulate = false; - parent::end(); - $this->db->simulate = true; } /** * Sets up a Dbo class instance for testing @@ -449,19 +435,17 @@ class MssqlTest extends CakeTestCase { * @return void */ function testDescribe() { - $MssqlTableDescription = array( - 0 => array( - 0 => array( - 'Default' => '((0))', - 'Field' => 'count', - 'Key' => 0, - 'Length' => '4', - 'Null' => 'NO', - 'Type' => 'integer', - ) + $MssqlTableDescription = new MssqlTestResultIterator(array( + (object) array( + 'Default' => '((0))', + 'Field' => 'count', + 'Key' => 0, + 'Length' => '4', + 'Null' => 'NO', + 'Type' => 'integer' ) - ); - $this->db->fetchAllResultsStack = array($MssqlTableDescription); + )); + $this->db->executeResultsStack = array($MssqlTableDescription); $dummyModel = $this->model; $result = $this->db->describe($dummyModel); $expected = array( @@ -480,12 +464,12 @@ class MssqlTest extends CakeTestCase { * @return unknown_type */ public function testBuildColumn() { - $column = array('name' => 'id', 'type' => 'integer', 'null' => '', 'default' => '', 'length' => '8', 'key' => 'primary'); + $column = array('name' => 'id', 'type' => 'integer', 'null' => false, 'default' => '', 'length' => '8', 'key' => 'primary'); $result = $this->db->buildColumn($column); $expected = '[id] int IDENTITY (1, 1) NOT NULL'; $this->assertEqual($expected, $result); - $column = array('name' => 'client_id', 'type' => 'integer', 'null' => '', 'default' => '0', 'length' => '11'); + $column = array('name' => 'client_id', 'type' => 'integer', 'null' => false, 'default' => '0', 'length' => '11'); $result = $this->db->buildColumn($column); $expected = '[client_id] int DEFAULT 0 NOT NULL'; $this->assertEqual($expected, $result); @@ -506,7 +490,7 @@ class MssqlTest extends CakeTestCase { $expected = '[name] varchar(255) NULL'; $this->assertEqual($expected, $result); - $column = array('name' => 'name', 'type' => 'string', 'null' => '', 'default' => '', 'length' => '255'); + $column = array('name' => 'name', 'type' => 'string', 'null' => false, 'default' => '', 'length' => '255'); $result = $this->db->buildColumn($column); $expected = '[name] varchar(255) DEFAULT \'\' NOT NULL'; $this->assertEqual($expected, $result); @@ -579,21 +563,13 @@ class MssqlTest extends CakeTestCase { * @return void */ public function testGetPrimaryKey() { - // When param is a model - $result = $this->db->getPrimaryKey($this->model); - $this->assertEqual($result, 'id'); - $schema = $this->model->schema(); - unset($schema['id']['key']); - $this->model->setSchema($schema); - $result = $this->db->getPrimaryKey($this->model); - $this->assertNull($result); - - // When param is a table name - $this->db->simulate = false; - $this->loadFixtures('Category'); - $result = $this->db->getPrimaryKey('categories'); + $result = $this->db->getPrimaryKey($schema); $this->assertEqual($result, 'id'); + + unset($schema['id']['key']); + $result = $this->db->getPrimaryKey($schema); + $this->assertNull($result); } /** @@ -603,51 +579,26 @@ class MssqlTest extends CakeTestCase { */ public function testInsertMulti() { $fields = array('id', 'name', 'login'); - $values = array('(1, \'Larry\', \'PhpNut\')', '(2, \'Renan\', \'renan.saddam\')'); + $values = array( + array(1, 'Larry', 'PhpNut'), + array(2, 'Renan', 'renan.saddam')); $this->db->simulated = array(); - $this->db->insertMulti($this->model, $fields, $values); + $this->db->insertMulti($this->model, $fields, $values, $this->model->schema()); $result = $this->db->simulated; $expected = array( 'SET IDENTITY_INSERT [mssql_test_models] ON', - 'INSERT INTO [mssql_test_models] ([id], [name], [login]) VALUES (1, \'Larry\', \'PhpNut\')', - 'INSERT INTO [mssql_test_models] ([id], [name], [login]) VALUES (2, \'Renan\', \'renan.saddam\')', 'SET IDENTITY_INSERT [mssql_test_models] OFF' ); $this->assertEqual($expected, $result); $fields = array('name', 'login'); - $values = array('(\'Larry\', \'PhpNut\')', '(\'Renan\', \'renan.saddam\')'); + $values = array( + array('Larry', 'PhpNut'), + array('Renan', 'renan.saddam')); $this->db->simulated = array(); - $this->db->insertMulti($this->model, $fields, $values); + $this->db->insertMulti($this->model, $fields, $values, $this->model->schema()); $result = $this->db->simulated; - $expected = array( - 'INSERT INTO [mssql_test_models] ([name], [login]) VALUES (\'Larry\', \'PhpNut\')', - 'INSERT INTO [mssql_test_models] ([name], [login]) VALUES (\'Renan\', \'renan.saddam\')' - ); + $expected = array(); $this->assertEqual($expected, $result); } -/** - * testLastError - * - * @return void - */ - public function testLastError() { - $debug = Configure::read('debug'); - Configure::write('debug', 0); - - $this->db->simulate = false; - $query = 'SELECT [name] FROM [categories]'; - $this->assertTrue($this->db->execute($query) !== false); - $this->assertNull($this->db->lastError()); - - $query = 'SELECT [inexistent_field] FROM [categories]'; - $this->assertFalse($this->db->execute($query)); - $this->assertNotNull($this->db->lastError()); - - $query = 'SELECT [name] FROM [categories]'; - $this->assertTrue($this->db->execute($query) !== false); - $this->assertNull($this->db->lastError()); - - Configure::write('debug', $debug); - } } diff --git a/lib/Cake/Test/Case/Model/ModelReadTest.php b/lib/Cake/Test/Case/Model/ModelReadTest.php index 84a4ea913..1d5f64c54 100644 --- a/lib/Cake/Test/Case/Model/ModelReadTest.php +++ b/lib/Cake/Test/Case/Model/ModelReadTest.php @@ -397,6 +397,10 @@ class ModelReadTest extends BaseModelTest { * @return void */ function testRecursiveUnbind() { + if ($this->skipIf($this->db instanceof Mssql, 'The test of testRecursiveUnbind test is not compatible with Mssql, because it check for time columns.')) { + return; + } + $this->loadFixtures('Apple', 'Sample'); $TestModel = new Apple(); $TestModel->recursive = 2; @@ -2992,7 +2996,7 @@ class ModelReadTest extends BaseModelTest { * @return void */ function testSelfAssociationAfterFind() { - $this->loadFixtures('Apple'); + $this->loadFixtures('Apple', 'Sample'); $afterFindModel = new NodeAfterFind(); $afterFindModel->recursive = 3; $afterFindData = $afterFindModel->find('all'); @@ -3651,6 +3655,10 @@ class ModelReadTest extends BaseModelTest { * @return void */ function testFindCombinedRelations() { + if ($this->skipIf($this->db instanceof Mssql, 'The test of testRecursiveUnbind test is not compatible with Mssql, because it check for time columns.')) { + return; + } + $this->loadFixtures('Apple', 'Sample'); $TestModel = new Apple(); @@ -6227,7 +6235,7 @@ class ModelReadTest extends BaseModelTest { // These tests are expected to fail on SQL Server since the LIMIT/OFFSET // hack can't handle small record counts. - if ($this->db instanceof Mssql) { + if (!($this->db instanceof Mssql)) { $result = $TestModel->find('all', array('limit' => 3, 'page' => 2)); $expected = array( array( @@ -7398,10 +7406,13 @@ class ModelReadTest extends BaseModelTest { $result = $Post->find('first'); $this->assertEqual($result['Post']['two'], 2); - $Post->Author->virtualFields = array('false' => '1 = 2'); - $result = $Post->find('first'); - $this->assertEqual($result['Post']['two'], 2); - $this->assertFalse((bool)$result['Author']['false']); + // SQL Server does not support operators in expressions + if (!($this->db instanceof Mssql)) { + $Post->Author->virtualFields = array('false' => '1 = 2'); + $result = $Post->find('first'); + $this->assertEqual($result['Post']['two'], 2); + $this->assertFalse((bool)$result['Author']['false']); + } $result = $Post->find('first',array('fields' => array('author_id'))); $this->assertFalse(isset($result['Post']['two'])); @@ -7470,7 +7481,7 @@ class ModelReadTest extends BaseModelTest { * */ public function testVirtualFieldsMysql() { - if ($this->skipIf(!($this->db instanceof Mysql), 'The rest of virtualFieds test is not compatible with Postgres')) { + if ($this->skipIf(!($this->db instanceof Mysql), 'The rest of virtualFieds test only compatible with Mysql')) { return; } $this->loadFixtures('Post', 'Author'); diff --git a/lib/Cake/TestSuite/Fixture/CakeTestFixture.php b/lib/Cake/TestSuite/Fixture/CakeTestFixture.php index 16bf36e0a..47ca89a49 100644 --- a/lib/Cake/TestSuite/Fixture/CakeTestFixture.php +++ b/lib/Cake/TestSuite/Fixture/CakeTestFixture.php @@ -211,7 +211,7 @@ class CakeTestFixture { $fields = array_keys($record); $values[] = array_values(array_merge($default, $record)); } - return $db->insertMulti($this->table, $fields, $values); + return $db->insertMulti($this->table, $fields, $values, $this->fields); } return true; } From c602fc845b1c69031686ba027a486bf4a0b6e358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renan=20Gon=C3=A7alves?= Date: Wed, 18 May 2011 20:59:17 +0200 Subject: [PATCH 15/32] Reverting the change to insertMulti, worked to solve the cache issue when trying to get the model schema. --- lib/Cake/Model/Datasource/Database/Mssql.php | 17 ++++++---- lib/Cake/Model/Datasource/DboSource.php | 3 +- lib/Cake/Model/Model.php | 2 +- .../Model/Datasource/Database/MssqlTest.php | 32 +++++++++++++++---- .../TestSuite/Fixture/CakeTestFixture.php | 2 +- 5 files changed, 38 insertions(+), 18 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 9dd4adcb4..685f11868 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -349,7 +349,7 @@ class Mssql extends DboSource { if (!empty($values)) { $fields = array_combine($fields, $values); } - $primaryKey = $this->_getPrimaryKey($model->schema()); + $primaryKey = $this->_getPrimaryKey($model); if (array_key_exists($primaryKey, $fields)) { if (empty($fields[$primaryKey])) { @@ -609,10 +609,9 @@ class Mssql extends DboSource { * @param string $table * @param string $fields * @param array $values - * @param array $schema */ - public function insertMulti($table, $fields, $values, $schema) { - $primaryKey = $this->_getPrimaryKey($schema); + public function insertMulti($table, $fields, $values) { + $primaryKey = $this->_getPrimaryKey($table); $hasPrimaryKey = $primaryKey != null && ( (is_array($fields) && in_array($primaryKey, $fields) || (is_string($fields) && strpos($fields, $this->startQuote . $primaryKey . $this->endQuote) !== false)) @@ -621,7 +620,7 @@ class Mssql extends DboSource { if ($hasPrimaryKey) { $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON'); } - parent::insertMulti($table, $fields, $values, $schema); + parent::insertMulti($table, $fields, $values); if ($hasPrimaryKey) { $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF'); } @@ -675,11 +674,15 @@ class Mssql extends DboSource { /** * Makes sure it will return the primary key * - * @param array $schema + * @param mixed $model Model instance of table name * @access protected * @return string */ - function _getPrimaryKey($schema) { + function _getPrimaryKey($model) { + if (!is_object($model)) { + $model = new Model(false, $model); + } + $schema = $this->describe($model); foreach ($schema as $field => $props) { if (isset($props['key']) && $props['key'] == 'primary') { return $field; diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index f796b1e3e..8d3db098b 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -2796,9 +2796,8 @@ class DboSource extends DataSource { * @param string $table * @param string $fields * @param array $values - * @param array $schema */ - public function insertMulti($table, $fields, $values, $schema) { + public function insertMulti($table, $fields, $values) { $table = $this->fullTableName($table); $holder = implode(',', array_fill(0, count($fields), '?')); $fields = implode(', ', array_map(array(&$this, 'name'), $fields)); diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index acf7bdd3f..63f1f6017 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -1522,7 +1522,7 @@ class Model extends Object { } if (!empty($newValues)) { - $db->insertMulti($this->{$join}, $fields, $newValues, $this->{$join}->schema()); + $db->insertMulti($this->{$join}, $fields, $newValues); } } } diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php index 63e14b261..f8cac70e0 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php @@ -91,13 +91,14 @@ class MssqlTestDb extends Mssql { /** * getPrimaryKey method * - * @param array $schema + * @param mixed $model * @access public * @return void */ - function getPrimaryKey($schema) { - return parent::_getPrimaryKey($schema); + function getPrimaryKey($model) { + return parent::_getPrimaryKey($model); } + /** * clearFieldMappings method * @@ -107,6 +108,17 @@ class MssqlTestDb extends Mssql { function clearFieldMappings() { $this->_fieldMappings = array(); } + +/** + * describe method + * + * @param object $model + * @access public + * @return void + */ + function describe($model) { + return empty($this->describe) ? parent::describe($model) : $this->describe; + } } /** @@ -312,6 +324,7 @@ class MssqlTest extends CakeTestCase { * @return void */ function tearDown() { + unset($this->db->describe); unset($this->model); } @@ -564,11 +577,14 @@ class MssqlTest extends CakeTestCase { */ public function testGetPrimaryKey() { $schema = $this->model->schema(); - $result = $this->db->getPrimaryKey($schema); + + $this->db->describe = $schema; + $result = $this->db->getPrimaryKey($this->model); $this->assertEqual($result, 'id'); unset($schema['id']['key']); - $result = $this->db->getPrimaryKey($schema); + $this->db->describe = $schema; + $result = $this->db->getPrimaryKey($this->model); $this->assertNull($result); } @@ -578,12 +594,14 @@ class MssqlTest extends CakeTestCase { * @return void */ public function testInsertMulti() { + $this->db->describe = $this->model->schema(); + $fields = array('id', 'name', 'login'); $values = array( array(1, 'Larry', 'PhpNut'), array(2, 'Renan', 'renan.saddam')); $this->db->simulated = array(); - $this->db->insertMulti($this->model, $fields, $values, $this->model->schema()); + $this->db->insertMulti($this->model, $fields, $values); $result = $this->db->simulated; $expected = array( 'SET IDENTITY_INSERT [mssql_test_models] ON', @@ -596,7 +614,7 @@ class MssqlTest extends CakeTestCase { array('Larry', 'PhpNut'), array('Renan', 'renan.saddam')); $this->db->simulated = array(); - $this->db->insertMulti($this->model, $fields, $values, $this->model->schema()); + $this->db->insertMulti($this->model, $fields, $values); $result = $this->db->simulated; $expected = array(); $this->assertEqual($expected, $result); diff --git a/lib/Cake/TestSuite/Fixture/CakeTestFixture.php b/lib/Cake/TestSuite/Fixture/CakeTestFixture.php index 47ca89a49..16bf36e0a 100644 --- a/lib/Cake/TestSuite/Fixture/CakeTestFixture.php +++ b/lib/Cake/TestSuite/Fixture/CakeTestFixture.php @@ -211,7 +211,7 @@ class CakeTestFixture { $fields = array_keys($record); $values[] = array_values(array_merge($default, $record)); } - return $db->insertMulti($this->table, $fields, $values, $this->fields); + return $db->insertMulti($this->table, $fields, $values); } return true; } From 375f86d2a6ee16ce987998c75db4b3fc4f6abf8f Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 21 May 2011 21:50:25 -0400 Subject: [PATCH 16/32] Changed the default empty value when create columns. --- lib/Cake/Model/Datasource/Database/Mssql.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 685f11868..5d967485e 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -636,7 +636,11 @@ class Mssql extends DboSource { function buildColumn($column) { $result = preg_replace('/(int|integer)\([0-9]+\)/i', '$1', parent::buildColumn($column)); if (strpos($result, 'DEFAULT NULL') !== false) { - $result = str_replace('DEFAULT NULL', 'NULL', $result); + if (isset($column['default']) && $column['default'] === '') { + $result = str_replace('DEFAULT NULL', "DEFAULT ''", $result); + } else { + $result = str_replace('DEFAULT NULL', 'NULL', $result); + } } else if (array_keys($column) == array('type', 'name')) { $result .= ' NULL'; } From 3e4cd4fdbbb812c3b2317f36cc7543a305ba3413 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 21 May 2011 22:10:05 -0400 Subject: [PATCH 17/32] Removed the simulate flag that is not used. --- .../Model/Datasource/Database/MssqlTest.php | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php index f8cac70e0..a77e3fc6a 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php @@ -35,14 +35,6 @@ class MssqlTestDb extends Mssql { */ public $simulated = array(); -/** - * simulate property - * - * @var array - * @access public - */ - public $simulate = false; - /** * execute results stack * @@ -59,12 +51,8 @@ class MssqlTestDb extends Mssql { * @return void */ function _execute($sql) { - if ($this->simulate) { - $this->simulated[] = $sql; - return empty($this->executeResultsStack) ? null : array_pop($this->executeResultsStack); - } else { - return parent::_execute($sql); - } + $this->simulated[] = $sql; + return empty($this->executeResultsStack) ? null : array_pop($this->executeResultsStack); } /** @@ -290,20 +278,6 @@ class MssqlTest extends CakeTestCase { */ public $fixtures = array('core.category'); -/** - * Make sure all fixtures tables are being created - * - */ - public function startTest($method) { - $this->db->simulate = true; - } -/** - * Make sure all fixtures tables are being dropped - * - */ - public function endTest($method) { - $this->db->simulate = false; - } /** * Sets up a Dbo class instance for testing * From 11d249e43b5c0603781e84b360d3adc8ab918b2f Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 21 May 2011 22:18:57 -0400 Subject: [PATCH 18/32] Updating PHPDoc and method visibility. --- lib/Cake/Model/Datasource/Database/Mssql.php | 39 +++++----- .../Model/Datasource/Database/MssqlTest.php | 72 +++++++------------ 2 files changed, 42 insertions(+), 69 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 5d967485e..b27b8cb18 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -93,7 +93,6 @@ class Mssql extends DboSource { * Index of basic SQL commands * * @var array - * @access protected */ protected $_commands = array( 'begin' => 'BEGIN TRANSACTION', @@ -105,7 +104,6 @@ class Mssql extends DboSource { * Define if the last query had error * * @var string - * @access private */ private $__lastQueryHadError = false; @@ -114,7 +112,7 @@ class Mssql extends DboSource { * * @return boolean True if the database could be connected, else false */ - function connect() { + public function connect() { $config = $this->config; $this->connected = false; try { @@ -180,7 +178,7 @@ class Mssql extends DboSource { * @param Model $model Model object to describe * @return array Fields in table. Keys are name and type */ - function describe($model) { + public function describe($model) { $cache = parent::describe($model); if ($cache != null) { return $cache; @@ -230,7 +228,7 @@ class Mssql extends DboSource { * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided * @return string Quoted and escaped data */ - function value($data, $column = null, $safe = false) { + public function value($data, $column = null, $safe = false) { $parent = parent::value($data, $column, $safe); if ($column === 'float' && strpos($data, '.') !== false) { @@ -279,7 +277,7 @@ class Mssql extends DboSource { * @param mixed $fields * @return array */ - function fields($model, $alias = null, $fields = array(), $quote = true) { + public function fields($model, $alias = null, $fields = array(), $quote = true) { if (empty($alias)) { $alias = $model->alias; } @@ -345,7 +343,7 @@ class Mssql extends DboSource { * @param mixed $conditions * @return array */ - function create($model, $fields = null, $values = null) { + public function create($model, $fields = null, $values = null) { if (!empty($values)) { $fields = array_combine($fields, $values); } @@ -375,7 +373,7 @@ class Mssql extends DboSource { * @param mixed $conditions * @return array */ - function update($model, $fields = array(), $values = null, $conditions = null) { + public function update($model, $fields = array(), $values = null, $conditions = null) { if (!empty($values)) { $fields = array_combine($fields, $values); } @@ -395,7 +393,7 @@ class Mssql extends DboSource { * @param integer $offset Offset from which to start results * @return string SQL limit/offset statement */ - function limit($limit, $offset = null) { + public function limit($limit, $offset = null) { if ($limit) { $rt = ''; if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) { @@ -416,7 +414,7 @@ class Mssql extends DboSource { * @param string $real Real database-layer column type (i.e. "varchar(255)") * @return string Abstract column type (i.e. "string") */ - function column($real) { + public function column($real) { if (is_array($real)) { $col = $real['name']; @@ -460,7 +458,7 @@ class Mssql extends DboSource { * * @param PDOStatement $results */ - function resultSet($results) { + public function resultSet($results) { $this->map = array(); $numFields = $results->columnCount(); $index = 0; @@ -492,7 +490,7 @@ class Mssql extends DboSource { * @param array $data Query data * @return string */ - function renderStatement($type, $data) { + public function renderStatement($type, $data) { switch (strtolower($type)) { case 'select': extract($data); @@ -542,9 +540,8 @@ class Mssql extends DboSource { * * @param string $order * @return string - * @access private */ - function __switchSort($order) { + private function __switchSort($order) { $order = preg_replace('/\s+ASC/i', '__tmp_asc__', $order); $order = preg_replace('/\s+DESC/i', ' ASC', $order); return preg_replace('/__tmp_asc__/', ' DESC', $order); @@ -555,9 +552,8 @@ class Mssql extends DboSource { * * @param string $sql A snippet of SQL representing an ORDER or WHERE statement * @return string The value of $sql with field names replaced - * @access private */ - function __mapFields($sql) { + private function __mapFields($sql) { if (empty($sql) || empty($this->_fieldMappings)) { return $sql; } @@ -576,7 +572,7 @@ class Mssql extends DboSource { * @param boolean $cache Enables returning/storing cached query results * @return array Array of resultset rows, or false if no rows matched */ - function read($model, $queryData = array(), $recursive = null) { + public function read($model, $queryData = array(), $recursive = null) { $results = parent::read($model, $queryData, $recursive); $this->_fieldMappings = array(); return $results; @@ -587,7 +583,7 @@ class Mssql extends DboSource { * * @return mixed */ - function fetchResult() { + public function fetchResult() { if ($row = $this->_result->fetch()) { $resultRow = array(); foreach ($this->map as $col => $meta) { @@ -633,7 +629,7 @@ class Mssql extends DboSource { * where options can be 'default', 'length', or 'key'. * @return string */ - function buildColumn($column) { + public function buildColumn($column) { $result = preg_replace('/(int|integer)\([0-9]+\)/i', '$1', parent::buildColumn($column)); if (strpos($result, 'DEFAULT NULL') !== false) { if (isset($column['default']) && $column['default'] === '') { @@ -654,7 +650,7 @@ class Mssql extends DboSource { * @param string $table * @return string */ - function buildIndex($indexes, $table = null) { + public function buildIndex($indexes, $table = null) { $join = array(); foreach ($indexes as $name => $value) { @@ -679,10 +675,9 @@ class Mssql extends DboSource { * Makes sure it will return the primary key * * @param mixed $model Model instance of table name - * @access protected * @return string */ - function _getPrimaryKey($model) { + protected function _getPrimaryKey($model) { if (!is_object($model)) { $model = new Model(false, $model); } diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php index a77e3fc6a..7b6b27f15 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php @@ -31,7 +31,6 @@ class MssqlTestDb extends Mssql { * simulated property * * @var array - * @access public */ public $simulated = array(); @@ -39,7 +38,6 @@ class MssqlTestDb extends Mssql { * execute results stack * * @var array - * @access public */ public $executeResultsStack = array(); @@ -47,10 +45,9 @@ class MssqlTestDb extends Mssql { * execute method * * @param mixed $sql - * @access protected - * @return void + * @return mixed */ - function _execute($sql) { + protected function _execute($sql) { $this->simulated[] = $sql; return empty($this->executeResultsStack) ? null : array_pop($this->executeResultsStack); } @@ -59,20 +56,18 @@ class MssqlTestDb extends Mssql { * fetchAll method * * @param mixed $sql - * @access protected * @return void */ - function _matchRecords($model, $conditions = null) { + protected function _matchRecords($model, $conditions = null) { return $this->conditions(array('id' => array(1, 2))); } /** * getLastQuery method * - * @access public - * @return void + * @return string */ - function getLastQuery() { + public function getLastQuery() { return $this->simulated[count($this->simulated) - 1]; } @@ -80,20 +75,18 @@ class MssqlTestDb extends Mssql { * getPrimaryKey method * * @param mixed $model - * @access public - * @return void + * @return string */ - function getPrimaryKey($model) { + public function getPrimaryKey($model) { return parent::_getPrimaryKey($model); } /** * clearFieldMappings method * - * @access public * @return void */ - function clearFieldMappings() { + public function clearFieldMappings() { $this->_fieldMappings = array(); } @@ -101,10 +94,9 @@ class MssqlTestDb extends Mssql { * describe method * * @param object $model - * @access public * @return void */ - function describe($model) { + public function describe($model) { return empty($this->describe) ? parent::describe($model) : $this->describe; } } @@ -120,7 +112,6 @@ class MssqlTestModel extends Model { * name property * * @var string 'MssqlTestModel' - * @access public */ public $name = 'MssqlTestModel'; @@ -128,7 +119,6 @@ class MssqlTestModel extends Model { * useTable property * * @var bool false - * @access public */ public $useTable = false; @@ -136,7 +126,6 @@ class MssqlTestModel extends Model { * _schema property * * @var array - * @access protected */ protected $_schema = array( 'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8', 'key' => 'primary'), @@ -163,7 +152,6 @@ class MssqlTestModel extends Model { * belongsTo property * * @var array - * @access public */ public $belongsTo = array( 'MssqlClientTestModel' => array( @@ -177,10 +165,9 @@ class MssqlTestModel extends Model { * @param mixed $fields * @param mixed $order * @param mixed $recursive - * @access public * @return void */ - function find($conditions = null, $fields = null, $order = null, $recursive = null) { + public function find($conditions = null, $fields = null, $order = null, $recursive = null) { return $conditions; } @@ -191,10 +178,9 @@ class MssqlTestModel extends Model { * @param mixed $fields * @param mixed $order * @param mixed $recursive - * @access public - * @return void + * @return array */ - function findAll($conditions = null, $fields = null, $order = null, $recursive = null) { + public function findAll($conditions = null, $fields = null, $order = null, $recursive = null) { return $conditions; } } @@ -209,21 +195,20 @@ class MssqlClientTestModel extends Model { * name property * * @var string 'MssqlAssociatedTestModel' - * @access public */ public $name = 'MssqlClientTestModel'; + /** * useTable property * * @var bool false - * @access public */ public $useTable = false; + /** * _schema property * * @var array - * @access protected */ protected $_schema = array( 'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8', 'key' => 'primary'), @@ -243,7 +228,7 @@ class MssqlTestResultIterator extends ArrayIterator { /** * closeCursor method * - * @access public + * @return void */ public function closeCursor() {} } @@ -259,7 +244,6 @@ class MssqlTest extends CakeTestCase { * The Dbo instance to be tested * * @var DboSource - * @access public */ public $db = null; @@ -267,14 +251,13 @@ class MssqlTest extends CakeTestCase { * autoFixtures property * * @var bool false - * @access public */ public $autoFixtures = false; + /** * fixtures property * * @var array - * @access public */ public $fixtures = array('core.category'); @@ -294,21 +277,20 @@ class MssqlTest extends CakeTestCase { /** * tearDown method * - * @access public * @return void */ - function tearDown() { - unset($this->db->describe); + public function tearDown() { + unset($this->Dbo); + unset($this->db); unset($this->model); } /** * testQuoting method * - * @access public * @return void */ - function testQuoting() { + public function testQuoting() { $expected = "1.2"; $result = $this->db->value(1.2, 'float'); $this->assertIdentical($expected, $result); @@ -332,10 +314,9 @@ class MssqlTest extends CakeTestCase { /** * testFields method * - * @access public * @return void */ - function testFields() { + public function testFields() { $fields = array( '[MssqlTestModel].[id] AS [MssqlTestModel__0]', '[MssqlTestModel].[client_id] AS [MssqlTestModel__1]', @@ -387,10 +368,9 @@ class MssqlTest extends CakeTestCase { /** * testDistinctFields method * - * @access public * @return void */ - function testDistinctFields() { + public function testDistinctFields() { $result = $this->db->fields($this->model, null, array('DISTINCT Car.country_code')); $expected = array('DISTINCT [Car].[country_code] AS [Car__0]'); $this->assertEqual($expected, $result); @@ -403,10 +383,9 @@ class MssqlTest extends CakeTestCase { /** * testDistinctWithLimit method * - * @access public * @return void */ - function testDistinctWithLimit() { + public function testDistinctWithLimit() { $this->db->read($this->model, array( 'fields' => array('DISTINCT MssqlTestModel.city', 'MssqlTestModel.country'), 'limit' => 5 @@ -418,10 +397,9 @@ class MssqlTest extends CakeTestCase { /** * testDescribe method * - * @access public * @return void */ - function testDescribe() { + public function testDescribe() { $MssqlTableDescription = new MssqlTestResultIterator(array( (object) array( 'Default' => '((0))', @@ -448,7 +426,7 @@ class MssqlTest extends CakeTestCase { /** * testBuildColumn * - * @return unknown_type + * @return void */ public function testBuildColumn() { $column = array('name' => 'id', 'type' => 'integer', 'null' => false, 'default' => '', 'length' => '8', 'key' => 'primary'); From 968fa1d5ef4e4da72340a10abe47f10fa22556c5 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 21 May 2011 22:40:30 -0400 Subject: [PATCH 19/32] Included an option to configure the prepate statement. SQL Server requires the cursor option to return the affected rows. --- lib/Cake/Model/Datasource/Database/Mssql.php | 15 +++++++++++++++ lib/Cake/Model/Datasource/DboSource.php | 5 +++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index b27b8cb18..db390bf12 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -689,4 +689,19 @@ class Mssql extends DboSource { } return null; } + +/** + * Executes given SQL statement. + * + * @param string $sql SQL statement + * @param array $params list of params to be bound to query + * @param array $prepareOptions Options to be used in the prepare statement + * @return PDOStatement if query executes with no problem, true as the result of a succesfull + * query returning no rows, suchs as a CREATE statement, false otherwise + */ + protected function _execute($sql, $params = array(), $prepareOptions = array()) { + $prepareOptions += array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL); + return parent::_execute($sql, $params, $prepareOptions); + } + } diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 8d3db098b..b40c682a3 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -450,10 +450,11 @@ class DboSource extends DataSource { * * @param string $sql SQL statement * @param array $params list of params to be bound to query + * @param array $prepareOptions Options to be used in the prepare statement * @return PDOStatement if query executes with no problem, true as the result of a succesfull * query returning no rows, suchs as a CREATE statement, false otherwise */ - protected function _execute($sql, $params = array()) { + protected function _execute($sql, $params = array(), $prepareOptions = array()) { $sql = trim($sql); if (preg_match('/^(?:CREATE|ALTER|DROP)/i', $sql)) { $statements = array_filter(explode(';', $sql)); @@ -464,7 +465,7 @@ class DboSource extends DataSource { } try { - $query = $this->_connection->prepare($sql); + $query = $this->_connection->prepare($sql, $prepareOptions); $query->setFetchMode(PDO::FETCH_LAZY); if (!$query->execute($params)) { $this->_results = $query; From ad67f3e1bc98f33eb0d2de8ed0f90d6d9630692c Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 22 May 2011 00:49:49 -0400 Subject: [PATCH 20/32] SQL Server PDO support prepare only to SELECT statements, others commands need to use exec method. --- lib/Cake/Model/Datasource/Database/Mssql.php | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index db390bf12..77d6cac9a 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -616,7 +616,16 @@ class Mssql extends DboSource { if ($hasPrimaryKey) { $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON'); } - parent::insertMulti($table, $fields, $values); + + $table = $this->fullTableName($table); + $fields = implode(', ', array_map(array(&$this, 'name'), $fields)); + $this->_connection->beginTransaction(); + foreach ($values as $value) { + $holder = implode(', ', array_map(array(&$this, 'value'), $value)); + $this->_execute("INSERT INTO {$table} ({$fields}) VALUES ({$holder})"); + } + $this->_connection->commit(); + if ($hasPrimaryKey) { $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF'); } @@ -694,14 +703,17 @@ class Mssql extends DboSource { * Executes given SQL statement. * * @param string $sql SQL statement - * @param array $params list of params to be bound to query + * @param array $params list of params to be bound to query (supported only in select) * @param array $prepareOptions Options to be used in the prepare statement * @return PDOStatement if query executes with no problem, true as the result of a succesfull * query returning no rows, suchs as a CREATE statement, false otherwise */ protected function _execute($sql, $params = array(), $prepareOptions = array()) { - $prepareOptions += array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL); - return parent::_execute($sql, $params, $prepareOptions); + if (strncasecmp($sql, 'SELECT', 6) == 0) { + $prepareOptions += array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL); + return parent::_execute($sql, $params, $prepareOptions); + } + return $this->_connection->exec($sql); } } From 82e2d9e271dbc8adf098a3d3e93a51eb4847b28f Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 22 May 2011 00:51:05 -0400 Subject: [PATCH 21/32] Updating model tests to support Mssql driver. --- .../Test/Case/Model/ModelIntegrationTest.php | 4 ++++ lib/Cake/Test/Case/Model/ModelReadTest.php | 23 +++++-------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php index 1b4d68024..8ff92174d 100644 --- a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php +++ b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php @@ -634,6 +634,8 @@ class ModelIntegrationTest extends BaseModelTest { * @return void */ function testDeconstructFieldsTime() { + $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $this->loadFixtures('Apple'); $TestModel = new Apple(); @@ -721,6 +723,8 @@ class ModelIntegrationTest extends BaseModelTest { * @return void */ function testDeconstructFieldsDateTime() { + $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $this->loadFixtures('Apple'); $TestModel = new Apple(); diff --git a/lib/Cake/Test/Case/Model/ModelReadTest.php b/lib/Cake/Test/Case/Model/ModelReadTest.php index 1d5f64c54..71f68af80 100644 --- a/lib/Cake/Test/Case/Model/ModelReadTest.php +++ b/lib/Cake/Test/Case/Model/ModelReadTest.php @@ -79,8 +79,8 @@ class ModelReadTest extends BaseModelTest { */ function testGroupBy() { $db = ConnectionManager::getDataSource('test'); - $isStrictGroupBy = $this->db instanceof Postgres || $this->db instanceof Sqlite || $this->db instanceof Oracle; - $message = 'Postgres and Oracle have strict GROUP BY and are incompatible with this test.'; + $isStrictGroupBy = $this->db instanceof Postgres || $this->db instanceof Sqlite || $this->db instanceof Oracle || $this->db instanceof Mssql; + $message = 'Postgres, Oracle, SQLite and SQL Server have strict GROUP BY and are incompatible with this test.'; if ($this->skipIf($isStrictGroupBy, $message )) { return; @@ -367,13 +367,6 @@ class ModelReadTest extends BaseModelTest { * @return void */ function testVeryStrangeUseCase() { - $message = "skipping SELECT * FROM ? WHERE ? = ? AND ? = ?; prepared query."; - $message .= " MSSQL is incompatible with this test."; - - if ($this->skipIf($this->db instanceof Mssql, $message)) { - return; - } - $this->loadFixtures('Article', 'User', 'Tag', 'ArticlesTag'); $Article = new Article(); @@ -3955,7 +3948,7 @@ class ModelReadTest extends BaseModelTest { $TestModel = new Basket(); $recursive = 3; - $result = $TestModel->find('all', compact('conditions', 'recursive')); + $result = $TestModel->find('all', compact('recursive')); $expected = array( array( @@ -6629,13 +6622,9 @@ class ModelReadTest extends BaseModelTest { * @return void */ function testFindCountDistinct() { - $skip = $this->skipIf( - $this->db instanceof Sqlite, - 'SELECT COUNT(DISTINCT field) is not compatible with SQLite' - ); - if ($skip) { - return; - } + $this->skipIf($this->db instanceof Sqlite, 'SELECT COUNT(DISTINCT field) is not compatible with SQLite'); + $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $this->loadFixtures('Project'); $TestModel = new Project(); $TestModel->create(array('name' => 'project')) && $TestModel->save(); From f0db55de6f72cb5675f6030f0744c2664ce6a5b6 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 22 May 2011 01:07:58 -0400 Subject: [PATCH 22/32] Using dbo methods to manage the transaction. --- lib/Cake/Model/Datasource/Database/Mssql.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index 77d6cac9a..a7922d67d 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -619,12 +619,12 @@ class Mssql extends DboSource { $table = $this->fullTableName($table); $fields = implode(', ', array_map(array(&$this, 'name'), $fields)); - $this->_connection->beginTransaction(); + $this->begin(); foreach ($values as $value) { $holder = implode(', ', array_map(array(&$this, 'value'), $value)); $this->_execute("INSERT INTO {$table} ({$fields}) VALUES ({$holder})"); } - $this->_connection->commit(); + $this->commit(); if ($hasPrimaryKey) { $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF'); From 195653bdfcb6df3b7bc846d0f298515949691a6b Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 22 May 2011 01:11:57 -0400 Subject: [PATCH 23/32] Updated the Mssql tests with fixes of insertMulti. --- lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php index 7b6b27f15..a252cf84f 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php @@ -557,6 +557,8 @@ class MssqlTest extends CakeTestCase { $result = $this->db->simulated; $expected = array( 'SET IDENTITY_INSERT [mssql_test_models] ON', + "INSERT INTO [mssql_test_models] ([id], [name], [login]) VALUES (1, 'Larry', 'PhpNut')", + "INSERT INTO [mssql_test_models] ([id], [name], [login]) VALUES (2, 'Renan', 'renan.saddam')", 'SET IDENTITY_INSERT [mssql_test_models] OFF' ); $this->assertEqual($expected, $result); @@ -568,7 +570,10 @@ class MssqlTest extends CakeTestCase { $this->db->simulated = array(); $this->db->insertMulti($this->model, $fields, $values); $result = $this->db->simulated; - $expected = array(); + $expected = array( + "INSERT INTO [mssql_test_models] ([name], [login]) VALUES ('Larry', 'PhpNut')", + "INSERT INTO [mssql_test_models] ([name], [login]) VALUES ('Renan', 'renan.saddam')", + ); $this->assertEqual($expected, $result); } } From e049e35b5d35a1f9b7cde4a65490e39c09c5115e Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 22 May 2011 22:16:22 -0400 Subject: [PATCH 24/32] Catching the error in execute for SQL Server to have the same behavior from the others drivers. --- lib/Cake/Model/Datasource/Database/Mssql.php | 11 +++++++++-- lib/Cake/Model/Datasource/DboSource.php | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Mssql.php index a7922d67d..fafdb6553 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Mssql.php @@ -705,7 +705,7 @@ class Mssql extends DboSource { * @param string $sql SQL statement * @param array $params list of params to be bound to query (supported only in select) * @param array $prepareOptions Options to be used in the prepare statement - * @return PDOStatement if query executes with no problem, true as the result of a succesfull + * @return mixed PDOStatement if query executes with no problem, true as the result of a succesfull, false on error * query returning no rows, suchs as a CREATE statement, false otherwise */ protected function _execute($sql, $params = array(), $prepareOptions = array()) { @@ -713,7 +713,14 @@ class Mssql extends DboSource { $prepareOptions += array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL); return parent::_execute($sql, $params, $prepareOptions); } - return $this->_connection->exec($sql); + try { + $this->_connection->exec($sql); + return true; + } catch (PDOException $e) { + $this->_results = null; + $this->error = $e->getMessage(); + return false; + } } } diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index b40c682a3..600b10625 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -451,7 +451,7 @@ class DboSource extends DataSource { * @param string $sql SQL statement * @param array $params list of params to be bound to query * @param array $prepareOptions Options to be used in the prepare statement - * @return PDOStatement if query executes with no problem, true as the result of a succesfull + * @return mixed PDOStatement if query executes with no problem, true as the result of a succesfull, false on error * query returning no rows, suchs as a CREATE statement, false otherwise */ protected function _execute($sql, $params = array(), $prepareOptions = array()) { From 76831863e3af12c45f08c217fea4b7610f452961 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 22 May 2011 22:21:01 -0400 Subject: [PATCH 25/32] Skipping tests that is not compatible with SQL Server. --- lib/Cake/Test/Case/Model/BehaviorCollectionTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php index 8643f6abc..017e8027d 100644 --- a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php +++ b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php @@ -606,6 +606,8 @@ class BehaviorCollectionTest extends CakeTestCase { * @return void */ function testBehaviorFindCallbacks() { + $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $Apple = new Apple(); $expected = $Apple->find('all'); @@ -805,6 +807,8 @@ class BehaviorCollectionTest extends CakeTestCase { * @return void */ function testBehaviorBelongsToFindCallbacks() { + $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $Apple = new Apple(); $Apple->unbindModel(array('hasMany' => array('Child'), 'hasOne' => array('Sample')), false); $expected = $Apple->find('all'); From 846634d7291e64fde97c17eda505b644bfd7bc4b Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 22 May 2011 22:50:29 -0400 Subject: [PATCH 26/32] Changed the test to be compatible with all tests. --- lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php b/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php index c423f979e..d6c24349c 100644 --- a/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php @@ -3420,7 +3420,7 @@ class ContainableBehaviorTest extends CakeTestCase { 'foreignKey' => 'article_id', 'associationForeignKey' => 'tag_id', // LENGHT function mysql-only, using LIKE does almost the same - 'conditions' => 'ShortTag.tag LIKE "???"' + 'conditions' => "ShortTag.tag LIKE '???'" ) ) ); From 39320ef352171aa26810b4d77c4d56421d7b08a2 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 22 May 2011 23:19:13 -0400 Subject: [PATCH 27/32] Renamed the Mssql driver to Sqlserver. --- app/Config/database.php.default | 4 +- .../Console/Command/Task/DbConfigTask.php | 2 +- .../skel/Config/database.php.default | 4 +- .../Database/{Mssql.php => Sqlserver.php} | 6 +- .../Model/Behavior/TranslateBehaviorTest.php | 4 +- .../Case/Model/BehaviorCollectionTest.php | 4 +- .../{MssqlTest.php => SqlserverTest.php} | 116 +++++++++--------- .../Test/Case/Model/ModelIntegrationTest.php | 4 +- lib/Cake/Test/Case/Model/ModelReadTest.php | 12 +- .../Test/Case/Network/CakeRequestTest.php | 4 +- 10 files changed, 80 insertions(+), 80 deletions(-) rename lib/Cake/Model/Datasource/Database/{Mssql.php => Sqlserver.php} (99%) rename lib/Cake/Test/Case/Model/Datasource/Database/{MssqlTest.php => SqlserverTest.php} (79%) diff --git a/app/Config/database.php.default b/app/Config/database.php.default index 81d3fa12c..234df8ea4 100644 --- a/app/Config/database.php.default +++ b/app/Config/database.php.default @@ -2,7 +2,7 @@ /** * This is core configuration file. * - * Use it to configure core behaviour ofCake. + * Use it to configure core behaviour of Cake. * * PHP 5 * @@ -31,7 +31,7 @@ * Database/Mysql - MySQL 4 & 5, * Database/Sqlite - SQLite (PHP5 only), * Database/Postgres - PostgreSQL 7 and higher, - * Database/Mssql - Microsoft SQL Server 2000 and higher, + * Database/Sqlserver - Microsoft SQL Server 2005 and higher, * Database/Oracle - Oracle 8 and higher * * You can add custom database drivers (or override existing drivers) by adding the diff --git a/lib/Cake/Console/Command/Task/DbConfigTask.php b/lib/Cake/Console/Command/Task/DbConfigTask.php index 7fb1064c8..e425f391f 100644 --- a/lib/Cake/Console/Command/Task/DbConfigTask.php +++ b/lib/Cake/Console/Command/Task/DbConfigTask.php @@ -104,7 +104,7 @@ class DbConfigTask extends Shell { } } - $driver = $this->in(__d('cake_console', 'Driver:'), array('Mssql', 'Mysql', 'Oracle', 'Postgres', 'Sqlite'), 'Mysql'); + $driver = $this->in(__d('cake_console', 'Driver:'), array('Mysql', 'Oracle', 'Postgres', 'Sqlite', 'Sqlserver'), 'Mysql'); $persistent = $this->in(__d('cake_console', 'Persistent Connection?'), array('y', 'n'), 'n'); if (strtolower($persistent) == 'n') { diff --git a/lib/Cake/Console/templates/skel/Config/database.php.default b/lib/Cake/Console/templates/skel/Config/database.php.default index 81d3fa12c..234df8ea4 100644 --- a/lib/Cake/Console/templates/skel/Config/database.php.default +++ b/lib/Cake/Console/templates/skel/Config/database.php.default @@ -2,7 +2,7 @@ /** * This is core configuration file. * - * Use it to configure core behaviour ofCake. + * Use it to configure core behaviour of Cake. * * PHP 5 * @@ -31,7 +31,7 @@ * Database/Mysql - MySQL 4 & 5, * Database/Sqlite - SQLite (PHP5 only), * Database/Postgres - PostgreSQL 7 and higher, - * Database/Mssql - Microsoft SQL Server 2000 and higher, + * Database/Sqlserver - Microsoft SQL Server 2005 and higher, * Database/Oracle - Oracle 8 and higher * * You can add custom database drivers (or override existing drivers) by adding the diff --git a/lib/Cake/Model/Datasource/Database/Mssql.php b/lib/Cake/Model/Datasource/Database/Sqlserver.php similarity index 99% rename from lib/Cake/Model/Datasource/Database/Mssql.php rename to lib/Cake/Model/Datasource/Database/Sqlserver.php index fafdb6553..669cdf631 100644 --- a/lib/Cake/Model/Datasource/Database/Mssql.php +++ b/lib/Cake/Model/Datasource/Database/Sqlserver.php @@ -1,6 +1,6 @@ 'Titel #1', 2 => 'Titel #2', 3 => 'Titel #3'); $this->assertEqual($expected, $result); - // MSSQL trigger an error and stops the page even if the debug = 0 - if ($this->db instanceof Mssql) { + // SQL Server trigger an error and stops the page even if the debug = 0 + if ($this->db instanceof Sqlserver) { $debug = Configure::read('debug'); Configure::write('debug', 0); diff --git a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php index 017e8027d..bce2d2a73 100644 --- a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php +++ b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php @@ -606,7 +606,7 @@ class BehaviorCollectionTest extends CakeTestCase { * @return void */ function testBehaviorFindCallbacks() { - $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $this->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.'); $Apple = new Apple(); $expected = $Apple->find('all'); @@ -807,7 +807,7 @@ class BehaviorCollectionTest extends CakeTestCase { * @return void */ function testBehaviorBelongsToFindCallbacks() { - $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $this->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.'); $Apple = new Apple(); $Apple->unbindModel(array('hasMany' => array('Child'), 'hasOne' => array('Sample')), false); diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php similarity index 79% rename from lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php rename to lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php index a252cf84f..7b4d7a7fa 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MssqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php @@ -1,6 +1,6 @@ array( + 'SqlserverClientTestModel' => array( 'foreignKey' => 'client_id' ) ); @@ -186,17 +186,17 @@ class MssqlTestModel extends Model { } /** - * MssqlClientTestModel class + * SqlserverClientTestModel class * * @package cake.tests.cases.libs.model.datasources */ -class MssqlClientTestModel extends Model { +class SqlserverClientTestModel extends Model { /** * name property * - * @var string 'MssqlAssociatedTestModel' + * @var string 'SqlserverAssociatedTestModel' */ - public $name = 'MssqlClientTestModel'; + public $name = 'SqlserverClientTestModel'; /** * useTable property @@ -220,11 +220,11 @@ class MssqlClientTestModel extends Model { } /** - * MssqlTestResultIterator class + * SqlserverTestResultIterator class * * @package cake.tests.cases.libs.model.datasources */ -class MssqlTestResultIterator extends ArrayIterator { +class SqlserverTestResultIterator extends ArrayIterator { /** * closeCursor method * @@ -234,11 +234,11 @@ class MssqlTestResultIterator extends ArrayIterator { } /** - * MssqlTest class + * SqlserverTest class * * @package cake.tests.cases.libs.model.datasources.dbo */ -class MssqlTest extends CakeTestCase { +class SqlserverTest extends CakeTestCase { /** * The Dbo instance to be tested @@ -267,11 +267,11 @@ class MssqlTest extends CakeTestCase { */ public function setUp() { $this->Dbo = ConnectionManager::getDataSource('test'); - if (!($this->Dbo instanceof Mssql)) { + if (!($this->Dbo instanceof Sqlserver)) { $this->markTestSkipped('Please configure the test datasource to use SQL Server.'); } - $this->db = new MssqlTestDb($this->Dbo->config); - $this->model = new MssqlTestModel(); + $this->db = new SqlserverTestDb($this->Dbo->config); + $this->model = new SqlserverTestModel(); } /** @@ -318,24 +318,24 @@ class MssqlTest extends CakeTestCase { */ public function testFields() { $fields = array( - '[MssqlTestModel].[id] AS [MssqlTestModel__0]', - '[MssqlTestModel].[client_id] AS [MssqlTestModel__1]', - '[MssqlTestModel].[name] AS [MssqlTestModel__2]', - '[MssqlTestModel].[login] AS [MssqlTestModel__3]', - '[MssqlTestModel].[passwd] AS [MssqlTestModel__4]', - '[MssqlTestModel].[addr_1] AS [MssqlTestModel__5]', - '[MssqlTestModel].[addr_2] AS [MssqlTestModel__6]', - '[MssqlTestModel].[zip_code] AS [MssqlTestModel__7]', - '[MssqlTestModel].[city] AS [MssqlTestModel__8]', - '[MssqlTestModel].[country] AS [MssqlTestModel__9]', - '[MssqlTestModel].[phone] AS [MssqlTestModel__10]', - '[MssqlTestModel].[fax] AS [MssqlTestModel__11]', - '[MssqlTestModel].[url] AS [MssqlTestModel__12]', - '[MssqlTestModel].[email] AS [MssqlTestModel__13]', - '[MssqlTestModel].[comments] AS [MssqlTestModel__14]', - 'CONVERT(VARCHAR(20), [MssqlTestModel].[last_login], 20) AS [MssqlTestModel__15]', - '[MssqlTestModel].[created] AS [MssqlTestModel__16]', - 'CONVERT(VARCHAR(20), [MssqlTestModel].[updated], 20) AS [MssqlTestModel__17]' + '[SqlserverTestModel].[id] AS [SqlserverTestModel__0]', + '[SqlserverTestModel].[client_id] AS [SqlserverTestModel__1]', + '[SqlserverTestModel].[name] AS [SqlserverTestModel__2]', + '[SqlserverTestModel].[login] AS [SqlserverTestModel__3]', + '[SqlserverTestModel].[passwd] AS [SqlserverTestModel__4]', + '[SqlserverTestModel].[addr_1] AS [SqlserverTestModel__5]', + '[SqlserverTestModel].[addr_2] AS [SqlserverTestModel__6]', + '[SqlserverTestModel].[zip_code] AS [SqlserverTestModel__7]', + '[SqlserverTestModel].[city] AS [SqlserverTestModel__8]', + '[SqlserverTestModel].[country] AS [SqlserverTestModel__9]', + '[SqlserverTestModel].[phone] AS [SqlserverTestModel__10]', + '[SqlserverTestModel].[fax] AS [SqlserverTestModel__11]', + '[SqlserverTestModel].[url] AS [SqlserverTestModel__12]', + '[SqlserverTestModel].[email] AS [SqlserverTestModel__13]', + '[SqlserverTestModel].[comments] AS [SqlserverTestModel__14]', + 'CONVERT(VARCHAR(20), [SqlserverTestModel].[last_login], 20) AS [SqlserverTestModel__15]', + '[SqlserverTestModel].[created] AS [SqlserverTestModel__16]', + 'CONVERT(VARCHAR(20), [SqlserverTestModel].[updated], 20) AS [SqlserverTestModel__17]' ); $result = $this->db->fields($this->model); @@ -343,7 +343,7 @@ class MssqlTest extends CakeTestCase { $this->assertEqual($expected, $result); $this->db->clearFieldMappings(); - $result = $this->db->fields($this->model, null, 'MssqlTestModel.*'); + $result = $this->db->fields($this->model, null, 'SqlserverTestModel.*'); $expected = $fields; $this->assertEqual($expected, $result); @@ -355,13 +355,13 @@ class MssqlTest extends CakeTestCase { $this->assertEqual($expected, $result); $this->db->clearFieldMappings(); - $result = $this->db->fields($this->model, null, array('*', 'MssqlClientTestModel.*')); + $result = $this->db->fields($this->model, null, array('*', 'SqlserverClientTestModel.*')); $expected = array_merge($fields, array( - '[MssqlClientTestModel].[id] AS [MssqlClientTestModel__18]', - '[MssqlClientTestModel].[name] AS [MssqlClientTestModel__19]', - '[MssqlClientTestModel].[email] AS [MssqlClientTestModel__20]', - 'CONVERT(VARCHAR(20), [MssqlClientTestModel].[created], 20) AS [MssqlClientTestModel__21]', - 'CONVERT(VARCHAR(20), [MssqlClientTestModel].[updated], 20) AS [MssqlClientTestModel__22]')); + '[SqlserverClientTestModel].[id] AS [SqlserverClientTestModel__18]', + '[SqlserverClientTestModel].[name] AS [SqlserverClientTestModel__19]', + '[SqlserverClientTestModel].[email] AS [SqlserverClientTestModel__20]', + 'CONVERT(VARCHAR(20), [SqlserverClientTestModel].[created], 20) AS [SqlserverClientTestModel__21]', + 'CONVERT(VARCHAR(20), [SqlserverClientTestModel].[updated], 20) AS [SqlserverClientTestModel__22]')); $this->assertEqual($expected, $result); } @@ -387,7 +387,7 @@ class MssqlTest extends CakeTestCase { */ public function testDistinctWithLimit() { $this->db->read($this->model, array( - 'fields' => array('DISTINCT MssqlTestModel.city', 'MssqlTestModel.country'), + 'fields' => array('DISTINCT SqlserverTestModel.city', 'SqlserverTestModel.country'), 'limit' => 5 )); $result = $this->db->getLastQuery(); @@ -400,7 +400,7 @@ class MssqlTest extends CakeTestCase { * @return void */ public function testDescribe() { - $MssqlTableDescription = new MssqlTestResultIterator(array( + $SqlserverTableDescription = new SqlserverTestResultIterator(array( (object) array( 'Default' => '((0))', 'Field' => 'count', @@ -410,7 +410,7 @@ class MssqlTest extends CakeTestCase { 'Type' => 'integer' ) )); - $this->db->executeResultsStack = array($MssqlTableDescription); + $this->db->executeResultsStack = array($SqlserverTableDescription); $dummyModel = $this->model; $result = $this->db->describe($dummyModel); $expected = array( @@ -512,13 +512,13 @@ class MssqlTest extends CakeTestCase { * @return void */ public function testUpdateAllSyntax() { - $fields = array('MssqlTestModel.client_id' => '[MssqlTestModel].[client_id] + 1'); - $conditions = array('MssqlTestModel.updated <' => date('2009-01-01 00:00:00')); + $fields = array('SqlserverTestModel.client_id' => '[SqlserverTestModel].[client_id] + 1'); + $conditions = array('SqlserverTestModel.updated <' => date('2009-01-01 00:00:00')); $this->db->update($this->model, $fields, null, $conditions); $result = $this->db->getLastQuery(); - $this->assertNoPattern('/MssqlTestModel/', $result); - $this->assertPattern('/^UPDATE \[mssql_test_models\]/', $result); + $this->assertNoPattern('/SqlserverTestModel/', $result); + $this->assertPattern('/^UPDATE \[sqlserver_test_models\]/', $result); $this->assertPattern('/SET \[client_id\] = \[client_id\] \+ 1/', $result); } @@ -556,10 +556,10 @@ class MssqlTest extends CakeTestCase { $this->db->insertMulti($this->model, $fields, $values); $result = $this->db->simulated; $expected = array( - 'SET IDENTITY_INSERT [mssql_test_models] ON', - "INSERT INTO [mssql_test_models] ([id], [name], [login]) VALUES (1, 'Larry', 'PhpNut')", - "INSERT INTO [mssql_test_models] ([id], [name], [login]) VALUES (2, 'Renan', 'renan.saddam')", - 'SET IDENTITY_INSERT [mssql_test_models] OFF' + 'SET IDENTITY_INSERT [sqlserver_test_models] ON', + "INSERT INTO [sqlserver_test_models] ([id], [name], [login]) VALUES (1, 'Larry', 'PhpNut')", + "INSERT INTO [sqlserver_test_models] ([id], [name], [login]) VALUES (2, 'Renan', 'renan.saddam')", + 'SET IDENTITY_INSERT [sqlserver_test_models] OFF' ); $this->assertEqual($expected, $result); @@ -571,8 +571,8 @@ class MssqlTest extends CakeTestCase { $this->db->insertMulti($this->model, $fields, $values); $result = $this->db->simulated; $expected = array( - "INSERT INTO [mssql_test_models] ([name], [login]) VALUES ('Larry', 'PhpNut')", - "INSERT INTO [mssql_test_models] ([name], [login]) VALUES ('Renan', 'renan.saddam')", + "INSERT INTO [sqlserver_test_models] ([name], [login]) VALUES ('Larry', 'PhpNut')", + "INSERT INTO [sqlserver_test_models] ([name], [login]) VALUES ('Renan', 'renan.saddam')", ); $this->assertEqual($expected, $result); } diff --git a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php index 8ff92174d..e9d4c66fa 100644 --- a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php +++ b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php @@ -634,7 +634,7 @@ class ModelIntegrationTest extends BaseModelTest { * @return void */ function testDeconstructFieldsTime() { - $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $this->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.'); $this->loadFixtures('Apple'); $TestModel = new Apple(); @@ -723,7 +723,7 @@ class ModelIntegrationTest extends BaseModelTest { * @return void */ function testDeconstructFieldsDateTime() { - $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $this->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.'); $this->loadFixtures('Apple'); $TestModel = new Apple(); diff --git a/lib/Cake/Test/Case/Model/ModelReadTest.php b/lib/Cake/Test/Case/Model/ModelReadTest.php index 71f68af80..f0518e83a 100644 --- a/lib/Cake/Test/Case/Model/ModelReadTest.php +++ b/lib/Cake/Test/Case/Model/ModelReadTest.php @@ -79,7 +79,7 @@ class ModelReadTest extends BaseModelTest { */ function testGroupBy() { $db = ConnectionManager::getDataSource('test'); - $isStrictGroupBy = $this->db instanceof Postgres || $this->db instanceof Sqlite || $this->db instanceof Oracle || $this->db instanceof Mssql; + $isStrictGroupBy = $this->db instanceof Postgres || $this->db instanceof Sqlite || $this->db instanceof Oracle || $this->db instanceof Sqlserver; $message = 'Postgres, Oracle, SQLite and SQL Server have strict GROUP BY and are incompatible with this test.'; if ($this->skipIf($isStrictGroupBy, $message )) { @@ -390,7 +390,7 @@ class ModelReadTest extends BaseModelTest { * @return void */ function testRecursiveUnbind() { - if ($this->skipIf($this->db instanceof Mssql, 'The test of testRecursiveUnbind test is not compatible with Mssql, because it check for time columns.')) { + if ($this->skipIf($this->db instanceof Sqlserver, 'The test of testRecursiveUnbind test is not compatible with SQL Server, because it check for time columns.')) { return; } @@ -3648,7 +3648,7 @@ class ModelReadTest extends BaseModelTest { * @return void */ function testFindCombinedRelations() { - if ($this->skipIf($this->db instanceof Mssql, 'The test of testRecursiveUnbind test is not compatible with Mssql, because it check for time columns.')) { + if ($this->skipIf($this->db instanceof Sqlserver, 'The test of testRecursiveUnbind test is not compatible with SQL Server, because it check for time columns.')) { return; } @@ -6228,7 +6228,7 @@ class ModelReadTest extends BaseModelTest { // These tests are expected to fail on SQL Server since the LIMIT/OFFSET // hack can't handle small record counts. - if (!($this->db instanceof Mssql)) { + if (!($this->db instanceof Sqlserver)) { $result = $TestModel->find('all', array('limit' => 3, 'page' => 2)); $expected = array( array( @@ -6623,7 +6623,7 @@ class ModelReadTest extends BaseModelTest { */ function testFindCountDistinct() { $this->skipIf($this->db instanceof Sqlite, 'SELECT COUNT(DISTINCT field) is not compatible with SQLite'); - $this->skipIf($this->db instanceof Mssql, 'This test is not compatible with Mssql.'); + $this->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.'); $this->loadFixtures('Project'); $TestModel = new Project(); @@ -7396,7 +7396,7 @@ class ModelReadTest extends BaseModelTest { $this->assertEqual($result['Post']['two'], 2); // SQL Server does not support operators in expressions - if (!($this->db instanceof Mssql)) { + if (!($this->db instanceof Sqlserver)) { $Post->Author->virtualFields = array('false' => '1 = 2'); $result = $Post->find('first'); $this->assertEqual($result['Post']['two'], 2); diff --git a/lib/Cake/Test/Case/Network/CakeRequestTest.php b/lib/Cake/Test/Case/Network/CakeRequestTest.php index 25c9c8ab2..589dee1f6 100644 --- a/lib/Cake/Test/Case/Network/CakeRequestTest.php +++ b/lib/Cake/Test/Case/Network/CakeRequestTest.php @@ -172,7 +172,7 @@ class CakeRequestTestCase extends CakeTestCase { function testFILESParsing() { $_FILES = array('data' => array('name' => array( 'File' => array( - array('data' => 'cake_mssql_patch.patch'), + array('data' => 'cake_sqlserver_patch.patch'), array('data' => 'controller.diff'), array('data' => ''), array('data' => ''), @@ -221,7 +221,7 @@ class CakeRequestTestCase extends CakeTestCase { $expected = array( 'File' => array( array('data' => array( - 'name' => 'cake_mssql_patch.patch', + 'name' => 'cake_sqlserver_patch.patch', 'type' => '', 'tmp_name' => '/private/var/tmp/phpy05Ywj', 'error' => 0, From 7bae41c4617637053cd0285770ac8e70f32a58a3 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 23 May 2011 00:22:08 -0400 Subject: [PATCH 28/32] Fixed the error check in queries and storing the right value to last affected in non-select queries. --- .../Model/Datasource/Database/Sqlserver.php | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Model/Datasource/Database/Sqlserver.php b/lib/Cake/Model/Datasource/Database/Sqlserver.php index 669cdf631..d68f2c9f6 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlserver.php +++ b/lib/Cake/Model/Datasource/Database/Sqlserver.php @@ -57,6 +57,13 @@ class Sqlserver extends DboSource { */ protected $_fieldMappings = array(); +/** + * Storing the last affected value + * + * @var mixed + */ + protected $_lastAffected = false; + /** * Base configuration settings for MS SQL driver * @@ -699,6 +706,19 @@ class Sqlserver extends DboSource { return null; } +/** + * Returns number of affected rows in previous database operation. If no previous operation exists, + * this returns false. + * + * @return integer Number of affected rows + */ + public function lastAffected() { + $affected = parent::lastAffected(); + if ($affected === null && $this->_lastAffected !== false) { + return $this->_lastAffected; + } + return $affected; + } /** * Executes given SQL statement. * @@ -709,12 +729,19 @@ class Sqlserver extends DboSource { * query returning no rows, suchs as a CREATE statement, false otherwise */ protected function _execute($sql, $params = array(), $prepareOptions = array()) { + $this->_lastAffected = false; if (strncasecmp($sql, 'SELECT', 6) == 0) { $prepareOptions += array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL); return parent::_execute($sql, $params, $prepareOptions); } try { - $this->_connection->exec($sql); + $this->_lastAffected = $this->_connection->exec($sql); + if ($this->_lastAffected === false) { + $this->_results = null; + $error = $this->_connection->errorInfo(); + $this->error = $error[2]; + return false; + } return true; } catch (PDOException $e) { $this->_results = null; From 0ae9974b3f1ae8de5a2986d4a2d879bb7d56970b Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 23 May 2011 00:31:21 -0400 Subject: [PATCH 29/32] Updating the test to be compatible with others dbo drivers. --- lib/Cake/Test/Case/Model/ModelWriteTest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Test/Case/Model/ModelWriteTest.php b/lib/Cake/Test/Case/Model/ModelWriteTest.php index 15224e6ad..95f8d834d 100644 --- a/lib/Cake/Test/Case/Model/ModelWriteTest.php +++ b/lib/Cake/Test/Case/Model/ModelWriteTest.php @@ -415,7 +415,12 @@ class ModelWriteTest extends BaseModelTest { } $this->loadFixtures('CategoryThread'); - $this->db->query('ALTER TABLE '. $this->db->fullTableName('category_threads') . " ADD COLUMN child_count INTEGER"); + $column = 'COLUMN '; + if ($this->db instanceof Sqlserver) { + $column = ''; + } + $column .= $this->db->buildColumn(array('name' => 'child_count', 'type' => 'integer')); + $this->db->query('ALTER TABLE '. $this->db->fullTableName('category_threads') . ' ADD ' . $column); $Category = new CategoryThread(); $result = $Category->updateAll(array('CategoryThread.name' => "'updated'"), array('CategoryThread.parent_id' => 5)); $this->assertFalse(empty($result)); @@ -424,7 +429,7 @@ class ModelWriteTest extends BaseModelTest { $Category->belongsTo['ParentCategory']['counterCache'] = 'child_count'; $Category->updateCounterCache(array('parent_id' => 5)); $result = Set::extract($Category->find('all', array('conditions' => array('CategoryThread.id' => 5))), '{n}.CategoryThread.child_count'); - $expected = array_fill(0, 1, 1); + $expected = array(1); $this->assertEqual($expected, $result); } From a744a74f1e86442aa9db65aedbe93930f1e87be0 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 23 May 2011 23:10:44 -0400 Subject: [PATCH 30/32] Not changing to NULL strings. --- lib/Cake/Model/Datasource/Database/Sqlserver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/Model/Datasource/Database/Sqlserver.php b/lib/Cake/Model/Datasource/Database/Sqlserver.php index d68f2c9f6..77a355853 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlserver.php +++ b/lib/Cake/Model/Datasource/Database/Sqlserver.php @@ -241,7 +241,7 @@ class Sqlserver extends DboSource { if ($column === 'float' && strpos($data, '.') !== false) { return rtrim($data, '0'); } - if ($parent === "''") { + if ($parent === "''" && ($column === null || $column !== 'string')) { return 'NULL'; } if ($parent != null) { From 02d4188a296d118cdc8572a6e0a35df419d6d993 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 23 May 2011 23:16:13 -0400 Subject: [PATCH 31/32] Skipping tests that is not compatible with SQL Server. --- lib/Cake/Test/Case/Model/ModelWriteTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Cake/Test/Case/Model/ModelWriteTest.php b/lib/Cake/Test/Case/Model/ModelWriteTest.php index 95f8d834d..b42ba2bd2 100644 --- a/lib/Cake/Test/Case/Model/ModelWriteTest.php +++ b/lib/Cake/Test/Case/Model/ModelWriteTest.php @@ -3834,6 +3834,8 @@ class ModelWriteTest extends BaseModelTest { * @return void */ function testUpdateAllEmptyValues() { + $this->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.'); + $this->loadFixtures('Author', 'Post'); $model = new Author(); $result = $model->updateAll(array('user' => '""')); @@ -3953,6 +3955,8 @@ class ModelWriteTest extends BaseModelTest { * @return void */ function testSaveAllEmptyData() { + $this->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.'); + $this->loadFixtures('Article', 'ProductUpdateAll', 'Comment', 'Attachment'); $model = new Article(); $result = $model->saveAll(array(), array('validate' => 'first')); From dee3efab49b13824d67f551e5fa48febeac3fb22 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 24 May 2011 00:07:38 -0400 Subject: [PATCH 32/32] Renaming the index name to be compatible with SQL Server. --- lib/Cake/Test/Fixture/ArticlesTagFixture.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/Test/Fixture/ArticlesTagFixture.php b/lib/Cake/Test/Fixture/ArticlesTagFixture.php index c475d87a4..c394f6ca9 100644 --- a/lib/Cake/Test/Fixture/ArticlesTagFixture.php +++ b/lib/Cake/Test/Fixture/ArticlesTagFixture.php @@ -41,7 +41,7 @@ class ArticlesTagFixture extends CakeTestFixture { public $fields = array( 'article_id' => array('type' => 'integer', 'null' => false), 'tag_id' => array('type' => 'integer', 'null' => false), - 'indexes' => array('UNIQUE_TAG' => array('column'=> array('article_id', 'tag_id'), 'unique'=>1)) + 'indexes' => array('UNIQUE_TAG2' => array('column'=> array('article_id', 'tag_id'), 'unique'=>1)) ); /**