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; }