‪TYPO3CMS  ‪main
PositionPlaceholderPreparedStatementTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Doctrine\DBAL\ArrayParameterType;
21 use Doctrine\DBAL\ParameterType;
22 use Doctrine\DBAL\Statement;
23 use PHPUnit\Framework\Attributes\DataProvider;
24 use PHPUnit\Framework\Attributes\Test;
28 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
32 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
33 
34 final class ‪PositionPlaceholderPreparedStatementTest extends FunctionalTestCase
35 {
36  protected function ‪setUp(): void
37  {
38  parent::setUp();
39  $this->importCSVDataSet(__DIR__ . '/Fixtures/DataSet/queryBuilder_preparedStatement.csv');
40  }
41 
42  #[Test]
43  public function ‪canBeInstantiated(): void
44  {
45  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
46  ->getQueryBuilderForTable('pages');
47 
48  self::assertIsObject($queryBuilder);
49  self::assertInstanceOf(QueryBuilder::class, $queryBuilder);
50  }
51 
52  #[Test]
54  {
55  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
56  ->getQueryBuilderForTable('pages');
57  $queryBuilder->getRestrictions()
58  ->removeAll()
59  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
60 
61  $statement = $queryBuilder
62  ->select(...['*'])
63  ->from('pages')
64  ->where(
65  $queryBuilder->expr()->eq(
66  'pid',
67  $queryBuilder->createPositionalParameter(10, ParameterType::INTEGER)
68  )
69  )
70  ->orderBy('sorting', 'ASC')
71  // add deterministic sort order as last sorting information, which many dbms
72  // and version does it by itself, but not all.
73  ->addOrderBy('uid', 'ASC')
74  ->prepare();
75 
76  // first execution of prepared statement
77  $result1 = $statement->executeQuery();
78  $rows1 = $result1->fetchAllAssociative();
79  self::assertSame(2, count($rows1));
80  self::assertSame(11, (int)($rows1[0]['uid'] ?? 0));
81  self::assertSame(12, (int)($rows1[1]['uid'] ?? 0));
82 
83  // second execution of prepared statement with changed placeholder value
84  $statement->bindValue(1, 20, ParameterType::INTEGER);
85  $result2 = $statement->executeQuery();
86  $rows2 = $result2->fetchAllAssociative();
87  self::assertSame(2, count($rows2));
88  self::assertSame(21, (int)($rows2[0]['uid'] ?? 0));
89  self::assertSame(22, (int)($rows2[1]['uid'] ?? 0));
90  }
91 
92  #[Test]
94  {
95  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
96  ->getQueryBuilderForTable('pages');
97  $queryBuilder->getRestrictions()
98  ->removeAll()
99  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
100 
101  $statement = $queryBuilder
102  ->select(...['*'])
103  ->from('pages')
104  ->where(
105  $queryBuilder->expr()->eq(
106  'pid',
107  $queryBuilder->createPositionalParameter(10, ParameterType::INTEGER)
108  )
109  )
110  ->orderBy('sorting', 'ASC')
111  // add deterministic sort order as last sorting information, which many dbms
112  // and version does it by itself, but not all.
113  ->addOrderBy('uid', 'ASC')
114  ->prepare();
115 
116  // first execution of prepared statement
117  $result1 = $statement->executeQuery();
118  $rows1 = [];
119  while ($row = $result1->fetchAssociative()) {
120  $rows1[] = $row;
121  }
122 
123  self::assertSame(2, count($rows1));
124  self::assertSame(11, (int)($rows1[0]['uid'] ?? 0));
125  self::assertSame(12, (int)($rows1[1]['uid'] ?? 0));
126 
127  // second execution of prepared statement with changed placeholder value
128  $statement->bindValue(1, 20, ParameterType::INTEGER);
129  $result2 = $statement->executeQuery();
130  $rows2 = [];
131  while ($row = $result2->fetchAssociative()) {
132  $rows2[] = $row;
133  }
134  self::assertSame(2, count($rows2));
135  self::assertSame(21, (int)($rows2[0]['uid'] ?? 0));
136  self::assertSame(22, (int)($rows2[1]['uid'] ?? 0));
137  }
138 
139  #[Test]
141  {
142  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
143  ->getQueryBuilderForTable('pages');
144  $queryBuilder->getRestrictions()
145  ->removeAll()
146  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
147 
148  $statement = $queryBuilder
149  ->select(...['*'])
150  ->from('pages')
151  ->where(
152  $queryBuilder->expr()->eq(
153  'pid',
154  $queryBuilder->createPositionalParameter(10, ParameterType::INTEGER)
155  )
156  )
157  ->orderBy('sorting', 'ASC')
158  // add deterministic sort order as last sorting information, which many dbms
159  // and version does it by itself, but not all.
160  ->addOrderBy('uid', 'ASC')
161  ->prepare();
162 
163  // first execution of prepared statement
164  $statement->bindValue(1, 10, ParameterType::INTEGER);
165  $result1 = $statement->executeQuery();
166  self::assertSame(11, (int)($result1->fetchAssociative()['uid'] ?? 0));
167 
168  // second execution of prepared statement with changed placeholder value
169  $statement->bindValue(1, 20, ParameterType::INTEGER);
170  $result2 = $statement->executeQuery();
171  self::assertSame(21, (int)($result2->fetchAssociative()['uid'] ?? 0));
172  }
173 
174  #[Test]
176  {
177  $runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime');
178  $cacheIdentifier = 'prepared-statement-through-runtime-cache';
179  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
180  ->getQueryBuilderForTable('pages');
181  $queryBuilder->getRestrictions()
182  ->removeAll()
183  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
184 
185  $statement = $queryBuilder
186  ->select(...['*'])
187  ->from('pages')
188  ->where(
189  $queryBuilder->expr()->eq(
190  'pid',
191  $queryBuilder->createPositionalParameter(10, ParameterType::INTEGER)
192  )
193  )
194  ->orderBy('sorting', 'ASC')
195  // add deterministic sort order as last sorting information, which many dbms
196  // and version does it by itself, but not all.
197  ->addOrderBy('uid', 'ASC')
198  ->prepare();
199  $runtimeCache->set($cacheIdentifier, $statement);
200 
201  // first execution of prepared statement
202  $statement->bindValue(1, 10, ParameterType::INTEGER);
203  $result1 = $statement->executeQuery();
204  self::assertSame(11, (int)($result1->fetchAssociative()['uid'] ?? 0));
205  unset($statement);
206 
207  // retrieve statement from runtime cache
208  $statement2 = $runtimeCache->get($cacheIdentifier);
209  self::assertInstanceOf(Statement::class, $statement2);
210 
211  // second execution of prepared statement with changed placeholder value
212  $statement2->bindValue(1, 20, ParameterType::INTEGER);
213  $result2 = $statement2->executeQuery();
214  self::assertSame(21, (int)($result2->fetchAssociative()['uid'] ?? 0));
215  unset($statement2);
216 
217  // We need to free used resultsets here to avoid a test-setup related issue with sqlite resulting in
218  // a locked db, using old database data because of os in-memory usage of overridden sqlite db file.
219  $result1->free();
220  $result2->free();
221  unset($result1, $result2);
222  }
223 
224  public static function ‪invalidParameterTypesForPreparedStatements(): array
225  {
226  return [
227  'PARAM_INT_ARRAY' => [‪Connection::PARAM_INT_ARRAY, 'PARAM_INT_ARRAY', [10, 20]],
228  'PARAM_STR_ARRAY' => [‪Connection::PARAM_STR_ARRAY, 'PARAM_STR_ARRAY', [10, 20]],
229  ];
230  }
231 
232  #[DataProvider('invalidParameterTypesForPreparedStatements')]
233  #[Test]
234  public function ‪preparedStatementThrowsExceptionForInvalidParameterType(ArrayParameterType $arrayParameterType, string $arrayParameterName, array $arrayValues): void
235  {
236  // expected exception
237  $this->expectExceptionObject(‪UnsupportedPreparedStatementParameterTypeException::new($arrayParameterName));
238 
239  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
240  ->getQueryBuilderForTable('pages');
241  $queryBuilder->getRestrictions()
242  ->removeAll()
243  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
244 
245  // prepare should result in exception, thus no further execution with it
246  $queryBuilder
247  ->select(...['*'])
248  ->from('pages')
249  ->where(
250  $queryBuilder->expr()->in(
251  'pid',
252  $queryBuilder->createPositionalParameter($arrayValues, $arrayParameterType)
253  )
254  )
255  ->orderBy('sorting', 'ASC')
256  // add deterministic sort order as last sorting information, which many dbms
257  // and version does it by itself, but not all.
258  ->addOrderBy('uid', 'ASC')
259  ->prepare();
260  }
261 }
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder\PositionPlaceholderPreparedStatementTest\preparedStatementWithPositionPlaceholderAndBindValueWithWileLoopWorks
‪preparedStatementWithPositionPlaceholderAndBindValueWithWileLoopWorks()
Definition: PositionPlaceholderPreparedStatementTest.php:93
‪TYPO3\CMS\Core\Database\Query\UnsupportedPreparedStatementParameterTypeException
Definition: UnsupportedPreparedStatementParameterTypeException.php:21
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder\PositionPlaceholderPreparedStatementTest\invalidParameterTypesForPreparedStatements
‪static invalidParameterTypesForPreparedStatements()
Definition: PositionPlaceholderPreparedStatementTest.php:224
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder
Definition: NamedPlaceholderPreparedStatementTest.php:18
‪TYPO3\CMS\Core\Database\Query\UnsupportedPreparedStatementParameterTypeException\new
‪static new(string $parameterType)
Definition: UnsupportedPreparedStatementParameterTypeException.php:22
‪TYPO3\CMS\Core\Database\Connection\PARAM_STR_ARRAY
‪const PARAM_STR_ARRAY
Definition: Connection.php:77
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder\PositionPlaceholderPreparedStatementTest\preparedStatementThrowsExceptionForInvalidParameterType
‪preparedStatementThrowsExceptionForInvalidParameterType(ArrayParameterType $arrayParameterType, string $arrayParameterName, array $arrayValues)
Definition: PositionPlaceholderPreparedStatementTest.php:234
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder\PositionPlaceholderPreparedStatementTest
Definition: PositionPlaceholderPreparedStatementTest.php:35
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder\PositionPlaceholderPreparedStatementTest\preparedStatementWorksIfRetrievedThroughRuntimeCacheAndPriorResultSetNotFreedAfterIncompleteDataRetrieval
‪preparedStatementWorksIfRetrievedThroughRuntimeCacheAndPriorResultSetNotFreedAfterIncompleteDataRetrieval()
Definition: PositionPlaceholderPreparedStatementTest.php:175
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder\PositionPlaceholderPreparedStatementTest\preparedStatementWithPositionPlaceholderAndBindValueWorks
‪preparedStatementWithPositionPlaceholderAndBindValueWorks()
Definition: PositionPlaceholderPreparedStatementTest.php:53
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder\PositionPlaceholderPreparedStatementTest\setUp
‪setUp()
Definition: PositionPlaceholderPreparedStatementTest.php:36
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder\PositionPlaceholderPreparedStatementTest\canBeInstantiated
‪canBeInstantiated()
Definition: PositionPlaceholderPreparedStatementTest.php:43
‪TYPO3\CMS\Core\Tests\Functional\Database\Query\QueryBuilder\PositionPlaceholderPreparedStatementTest\preparedStatementWithoutRetrievingFullResultSetAndWithoutFreeingPriorResultSetWorks
‪preparedStatementWithoutRetrievingFullResultSetAndWithoutFreeingPriorResultSetWorks()
Definition: PositionPlaceholderPreparedStatementTest.php:140
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT_ARRAY
‪const PARAM_INT_ARRAY
Definition: Connection.php:72