[BUGFIX] dbal: make admin_get_tables() return correct table information
[Packages/TYPO3.CMS.git] / typo3 / sysext / dbal / Tests / Unit / Database / DatabaseConnectionTest.php
1 <?php
2 namespace TYPO3\CMS\Dbal\Tests\Unit\Database;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * Test case
21 */
22 class DatabaseConnectionTest extends AbstractTestCase {
23
24 /**
25 * @var \TYPO3\CMS\Dbal\Database\DatabaseConnection|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
26 */
27 protected $subject;
28
29 /**
30 * Set up
31 */
32 protected function setUp() {
33 $GLOBALS['TYPO3_LOADED_EXT'] = array();
34
35 /** @var \TYPO3\CMS\Dbal\Database\DatabaseConnection|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject */
36 $subject = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\DatabaseConnection::class, array('getFieldInfoCache'), array(), '', FALSE);
37
38 // Disable caching
39 $mockCacheFrontend = $this->getMock(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend::class, array(), array(), '', FALSE);
40 $subject->expects($this->any())->method('getFieldInfoCache')->will($this->returnValue($mockCacheFrontend));
41
42 // Inject SqlParser - Its logic is tested with the tests, too.
43 $sqlParser = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\SqlParser::class, array('dummy'), array(), '', FALSE);
44 $sqlParser->_set('databaseConnection', $subject);
45 $subject->SQLparser = $sqlParser;
46
47 // Mock away schema migration service from install tool
48 $installerSqlMock = $this->getMock(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService::class, array('getFieldDefinitions_fileContent'), array(), '', FALSE);
49 $installerSqlMock->expects($this->any())->method('getFieldDefinitions_fileContent')->will($this->returnValue(array()));
50 $subject->_set('installerSql', $installerSqlMock);
51
52 $subject->initialize();
53 $subject->lastHandlerKey = '_DEFAULT';
54
55 $this->subject = $subject;
56 }
57
58 /**
59 * Creates a fake extension with a given table definition.
60 *
61 * @param string $tableDefinition SQL script to create the extension's tables
62 * @throws \RuntimeException
63 * @return void
64 */
65 protected function createFakeExtension($tableDefinition) {
66 // Prepare a fake extension configuration
67 $ext_tables = GeneralUtility::tempnam('ext_tables');
68 if (!GeneralUtility::writeFile($ext_tables, $tableDefinition)) {
69 throw new \RuntimeException('Can\'t write temporary ext_tables file.');
70 }
71 $this->testFilesToDelete[] = $ext_tables;
72 $GLOBALS['TYPO3_LOADED_EXT'] = array(
73 'test_dbal' => array(
74 'ext_tables.sql' => $ext_tables
75 )
76 );
77 // Append our test table to the list of existing tables
78 $this->subject->initialize();
79 }
80
81 /**
82 * @test
83 */
84 public function tableWithMappingIsDetected() {
85 $dbalConfiguration = array(
86 'mapping' => array(
87 'cf_cache_hash' => array(),
88 ),
89 );
90
91 /** @var \TYPO3\CMS\Dbal\Database\DatabaseConnection|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject */
92 $subject = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\DatabaseConnection::class, array('getFieldInfoCache'), array(), '', FALSE);
93
94 $mockCacheFrontend = $this->getMock(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend::class, array(), array(), '', FALSE);
95 $subject->expects($this->any())->method('getFieldInfoCache')->will($this->returnValue($mockCacheFrontend));
96
97 $sqlParser = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\SqlParser::class, array('dummy'), array(), '', FALSE);
98 $sqlParser->_set('databaseConnection', $subject);
99 $subject->SQLparser = $sqlParser;
100
101 $installerSqlMock = $this->getMock(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService::class, array(), array(), '', FALSE);
102 $subject->_set('installerSql', $installerSqlMock);
103 $schemaMigrationResult = array(
104 'cf_cache_pages' => array(),
105 );
106 $installerSqlMock->expects($this->once())->method('getFieldDefinitions_fileContent')->will($this->returnValue($schemaMigrationResult));
107
108 $subject->conf = $dbalConfiguration;
109 $subject->initialize();
110 $subject->lastHandlerKey = '_DEFAULT';
111
112 $this->assertFalse($subject->_call('map_needMapping', 'cf_cache_pages'));
113 $cfCacheHashNeedsMapping = $subject->_call('map_needMapping', 'cf_cache_hash');
114 $this->assertEquals('cf_cache_hash', $cfCacheHashNeedsMapping[0]['table']);
115 }
116
117 /**
118 * @test
119 * @see https://forge.typo3.org/issues/67067
120 */
121 public function adminGetTablesReturnsArrayWithNameKey() {
122 $handlerMock = $this->getMock('\ADODB_mock', array('MetaTables'), array(), '', FALSE);
123 $handlerMock->expects($this->any())->method('MetaTables')->will($this->returnValue(array('cf_cache_hash')));
124 $this->subject->handlerCfg['_DEFAULT']['type'] = 'adodb';
125 $this->subject->handlerInstance['_DEFAULT'] = $handlerMock;
126
127 $actual = $this->subject->admin_get_tables();
128 $expected = array('cf_cache_hash' => array('Name' => 'cf_cache_hash'));
129 $this->assertSame($expected, $actual);
130 }
131
132 /**
133 * @test
134 * @see http://forge.typo3.org/issues/21502
135 */
136 public function concatCanBeParsedAfterLikeOperator() {
137 $result = $this->subject->SELECTquery('*', 'sys_refindex, tx_dam_file_tracking', 'sys_refindex.tablename = \'tx_dam_file_tracking\'' . ' AND sys_refindex.ref_string LIKE CONCAT(tx_dam_file_tracking.file_path, tx_dam_file_tracking.file_name)');
138 $expected = 'SELECT * FROM sys_refindex, tx_dam_file_tracking WHERE sys_refindex.tablename = \'tx_dam_file_tracking\'';
139 $expected .= ' AND sys_refindex.ref_string LIKE CONCAT(tx_dam_file_tracking.file_path, tx_dam_file_tracking.file_name)';
140 $this->assertEquals($expected, $this->cleanSql($result));
141 }
142
143 /**
144 * @test
145 * @see http://forge.typo3.org/issues/20346
146 */
147 public function floatNumberCanBeStoredInDatabase() {
148 $this->createFakeExtension('
149 CREATE TABLE tx_test_dbal (
150 foo double default \'0\',
151 foobar int default \'0\'
152 );
153 ');
154 $data = array(
155 'foo' => 99.12,
156 'foobar' => -120
157 );
158 $result = $this->subject->INSERTquery('tx_test_dbal', $data);
159 $expected = 'INSERT INTO tx_test_dbal ( foo, foobar ) VALUES ( \'99.12\', \'-120\' )';
160 $this->assertEquals($expected, $this->cleanSql($result));
161 }
162
163 /**
164 * @test
165 * @see http://forge.typo3.org/issues/20427
166 */
167 public function positive64BitIntegerIsSupported() {
168 if (!is_int(9223372036854775806)) {
169 $this->markTestSkipped('Test skipped because running on 32 bit system.');
170 }
171 $this->createFakeExtension('
172 CREATE TABLE tx_test_dbal (
173 foo int default \'0\',
174 foobar bigint default \'0\'
175 );
176 ');
177 $data = array(
178 'foo' => 9223372036854775807,
179 'foobar' => 9223372036854775807
180 );
181 $result = $this->subject->INSERTquery('tx_test_dbal', $data);
182 $expected = 'INSERT INTO tx_test_dbal ( foo, foobar ) VALUES ( \'9223372036854775807\', \'9223372036854775807\' )';
183 $this->assertEquals($expected, $this->cleanSql($result));
184 }
185
186 /**
187 * @test
188 */
189 public function sqlForInsertWithMultipleRowsIsValid() {
190 $fields = array('uid', 'pid', 'title', 'body');
191 $rows = array(
192 array('1', '2', 'Title #1', 'Content #1'),
193 array('3', '4', 'Title #2', 'Content #2'),
194 array('5', '6', 'Title #3', 'Content #3')
195 );
196 $result = $this->subject->INSERTmultipleRows('tt_content', $fields, $rows);
197 $expected = 'INSERT INTO tt_content (uid, pid, title, body) VALUES ';
198 $expected .= '(\'1\', \'2\', \'Title #1\', \'Content #1\'), ';
199 $expected .= '(\'3\', \'4\', \'Title #2\', \'Content #2\'), ';
200 $expected .= '(\'5\', \'6\', \'Title #3\', \'Content #3\')';
201 $this->assertEquals($expected, $this->cleanSql($result));
202 }
203
204 /**
205 * @test
206 * @see http://forge.typo3.org/issues/16708
207 */
208 public function minFunctionAndInOperatorCanBeParsed() {
209 $result = $this->subject->SELECTquery('*', 'pages', 'MIN(uid) IN (1,2,3,4)');
210 $expected = 'SELECT * FROM pages WHERE MIN(uid) IN (1,2,3,4)';
211 $this->assertEquals($expected, $this->cleanSql($result));
212 }
213
214 /**
215 * @test
216 * @see http://forge.typo3.org/issues/16708
217 */
218 public function maxFunctionAndInOperatorCanBeParsed() {
219 $result = $this->subject->SELECTquery('*', 'pages', 'MAX(uid) IN (1,2,3,4)');
220 $expected = 'SELECT * FROM pages WHERE MAX(uid) IN (1,2,3,4)';
221 $this->assertEquals($expected, $this->cleanSql($result));
222 }
223
224 /**
225 * @test
226 * @see http://forge.typo3.org/issues/21514
227 */
228 public function likeBinaryOperatorIsKept() {
229 $result = $this->cleanSql($this->subject->SELECTquery('*', 'tt_content', 'bodytext LIKE BINARY \'test\''));
230 $expected = 'SELECT * FROM tt_content WHERE bodytext LIKE BINARY \'test\'';
231 $this->assertEquals($expected, $this->cleanSql($result));
232 }
233
234 /**
235 * @test
236 * @see http://forge.typo3.org/issues/21514
237 */
238 public function notLikeBinaryOperatorIsKept() {
239 $result = $this->cleanSql($this->subject->SELECTquery('*', 'tt_content', 'bodytext NOT LIKE BINARY \'test\''));
240 $expected = 'SELECT * FROM tt_content WHERE bodytext NOT LIKE BINARY \'test\'';
241 $this->assertEquals($expected, $this->cleanSql($result));
242 }
243
244 ///////////////////////////////////////
245 // Tests concerning prepared queries
246 ///////////////////////////////////////
247 /**
248 * @test
249 * @see http://forge.typo3.org/issues/23374
250 */
251 public function similarNamedParametersAreProperlyReplaced() {
252 $sql = 'SELECT * FROM cache WHERE tag = :tag1 OR tag = :tag10 OR tag = :tag100';
253 $parameterValues = array(
254 ':tag1' => 'tag-one',
255 ':tag10' => 'tag-two',
256 ':tag100' => 'tag-three'
257 );
258 $className = self::buildAccessibleProxy(\TYPO3\CMS\Core\Database\PreparedStatement::class);
259 $query = $sql;
260 $precompiledQueryParts = array();
261 $statement = new $className($sql, 'cache');
262 $statement->bindValues($parameterValues);
263 $parameters = $statement->_get('parameters');
264 $statement->_callRef('convertNamedPlaceholdersToQuestionMarks', $query, $parameters, $precompiledQueryParts);
265 $expectedQuery = 'SELECT * FROM cache WHERE tag = ? OR tag = ? OR tag = ?';
266 $expectedParameterValues = array(
267 0 => array(
268 'type' => \TYPO3\CMS\Core\Database\PreparedStatement::PARAM_STR,
269 'value' => 'tag-one',
270 ),
271 1 => array(
272 'type' => \TYPO3\CMS\Core\Database\PreparedStatement::PARAM_STR,
273 'value' => 'tag-two',
274 ),
275 2 => array(
276 'type' => \TYPO3\CMS\Core\Database\PreparedStatement::PARAM_STR,
277 'value' => 'tag-three',
278 ),
279 );
280 $this->assertEquals($expectedQuery, $query);
281 $this->assertEquals($expectedParameterValues, $parameters);
282 }
283
284 }