[TASK] Extend Testbase to run functionals tests with multiple DB drivers
[Packages/TYPO3.CMS.git] / components / testing_framework / Classes / Core / Testbase.php
1 <?php
2 namespace TYPO3\Components\TestingFramework\Core;
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 Doctrine\DBAL\DBALException;
18 use Doctrine\DBAL\DriverManager;
19 use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
20 use TYPO3\CMS\Core\Core\Bootstrap;
21 use TYPO3\CMS\Core\Database\Connection;
22 use TYPO3\CMS\Core\Database\ConnectionPool;
23 use TYPO3\CMS\Core\Database\Schema\SchemaMigrator;
24 use TYPO3\CMS\Core\Database\Schema\SqlReader;
25 use TYPO3\CMS\Core\Utility\ArrayUtility;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27
28 /**
29 * This is a helper class used by unit, functional and acceptance test
30 * environment builders.
31 * It contains methods to create test environments.
32 *
33 * This class is for internal use only and may change wihtout further notice.
34 *
35 * Use the classes "UnitTestCase", "FunctionalTestCase" or "AcceptanceCoreEnvironment"
36 * to indirectly benefit from this class in own extensions.
37 */
38 class Testbase
39 {
40 /**
41 * This class must be called in CLI environment as a security measure
42 * against path disclosures and other stuff. Check this within
43 * constructor to make sure this check can't be circumvented.
44 */
45 public function __construct()
46 {
47 // Ensure cli only as security measure
48 if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
49 die('This script supports command line usage only. Please check your command.');
50 }
51 }
52
53 /**
54 * Makes sure error messages during the tests get displayed no matter what is set in php.ini.
55 *
56 * @return void
57 */
58 public function enableDisplayErrors()
59 {
60 @ini_set('display_errors', 1);
61 }
62
63 /**
64 * Defines a list of basic constants that are used by GeneralUtility and other
65 * helpers during tests setup. Those are sanitized in SystemEnvironmentBuilder
66 * to be not defined again.
67 *
68 * @return void
69 * @see SystemEnvironmentBuilder::defineBaseConstants()
70 */
71 public function defineBaseConstants()
72 {
73 // A null, a tabulator, a linefeed, a carriage return, a substitution, a CR-LF combination
74 defined('NUL') ?: define('NUL', chr(0));
75 defined('TAB') ?: define('TAB', chr(9));
76 defined('LF') ?: define('LF', chr(10));
77 defined('CR') ?: define('CR', chr(13));
78 defined('SUB') ?: define('SUB', chr(26));
79 defined('CRLF') ?: define('CRLF', CR . LF);
80
81 if (!defined('TYPO3_OS')) {
82 // Operating system identifier
83 // Either "WIN" or empty string
84 $typoOs = '';
85 if (!stristr(PHP_OS, 'darwin') && !stristr(PHP_OS, 'cygwin') && stristr(PHP_OS, 'win')) {
86 $typoOs = 'WIN';
87 }
88 define('TYPO3_OS', $typoOs);
89 }
90 }
91
92 /**
93 * Defines the PATH_site and PATH_thisScript constant and sets $_SERVER['SCRIPT_NAME'].
94 * For unit tests only
95 *
96 * @return void
97 */
98 public function defineSitePath()
99 {
100 define('PATH_site', $this->getWebRoot());
101 define('PATH_thisScript', PATH_site . 'typo3/cli_dispatch.phpsh');
102 $_SERVER['SCRIPT_NAME'] = PATH_thisScript;
103
104 if (!file_exists(PATH_thisScript)) {
105 $this->exitWithMessage('Unable to determine path to entry script. Please check your path or set an environment variable \'TYPO3_PATH_WEB\' to your root path.');
106 }
107 }
108
109 /**
110 * Defines the constant ORIGINAL_ROOT for the path to the original TYPO3 document root.
111 * For functional / acceptance tests only
112 * If ORIGINAL_ROOT already is defined, this method is a no-op.
113 *
114 * @return void
115 */
116 public function defineOriginalRootPath()
117 {
118 if (!defined('ORIGINAL_ROOT')) {
119 define('ORIGINAL_ROOT', $this->getWebRoot());
120 }
121
122 if (!file_exists(ORIGINAL_ROOT . 'typo3/cli_dispatch.phpsh')) {
123 $this->exitWithMessage('Unable to determine path to entry script. Please check your path or set an environment variable \'TYPO3_PATH_WEB\' to your root path.');
124 }
125 }
126
127 /**
128 * Define TYPO3_MODE to BE
129 *
130 * @return void
131 */
132 public function defineTypo3ModeBe()
133 {
134 define('TYPO3_MODE', 'BE');
135 }
136
137 /**
138 * Sets the environment variable TYPO3_CONTEXT to testing.
139 *
140 * @return void
141 */
142 public function setTypo3TestingContext()
143 {
144 putenv('TYPO3_CONTEXT=Testing');
145 }
146
147 /**
148 * Creates directories, recursively if required.
149 *
150 * @param string $directory Absolute path to directories to create
151 * @return void
152 * @throws Exception
153 */
154 public function createDirectory($directory)
155 {
156 if (is_dir($directory)) {
157 return;
158 }
159 @mkdir($directory, 0777, true);
160 clearstatcache();
161 if (!is_dir($directory)) {
162 throw new Exception('Directory "' . $directory . '" could not be created', 1404038665);
163 }
164 }
165
166 /**
167 * Checks whether given test instance exists in path and is younger than some minutes.
168 * Used in functional tests
169 *
170 * @param string $instancePath Absolute path to test instance
171 * @return bool
172 */
173 public function recentTestInstanceExists($instancePath)
174 {
175 if (@file_get_contents($instancePath . '/last_run.txt') <= (time() - 300)) {
176 return false;
177 } else {
178 // Test instance exists and is pretty young -> re-use
179 return true;
180 }
181 }
182
183 /**
184 * Remove test instance folder structure if it exists.
185 * This may happen if a functional test before threw a fatal or is too old
186 *
187 * @param string $instancePath Absolute path to test instance
188 * @return void
189 * @throws Exception
190 */
191 public function removeOldInstanceIfExists($instancePath)
192 {
193 if (is_dir($instancePath)) {
194 $success = GeneralUtility::rmdir($instancePath, true);
195 if (!$success) {
196 throw new Exception(
197 'Can not remove folder: ' . $instancePath,
198 1376657210
199 );
200 }
201 }
202 }
203
204 /**
205 * Create last_run.txt file within instance path containing timestamp of "now".
206 * Used in functional tests to reuse an instance for multiple tests in one test case.
207 *
208 * @param string $instancePath Absolute path to test instance
209 * @return void
210 */
211 public function createLastRunTextfile($instancePath)
212 {
213 // Store the time instance was created
214 file_put_contents($instancePath . '/last_run.txt', time());
215 }
216
217 /**
218 * Link TYPO3 CMS core from "parent" instance.
219 * For functional and acceptance tests.
220 *
221 * @param string $instancePath Absolute path to test instance
222 * @throws Exception
223 * @return void
224 */
225 public function setUpInstanceCoreLinks($instancePath)
226 {
227 $linksToSet = [
228 ORIGINAL_ROOT . 'typo3' => $instancePath . '/typo3',
229 ORIGINAL_ROOT . 'index.php' => $instancePath . '/index.php'
230 ];
231 foreach ($linksToSet as $from => $to) {
232 $success = symlink($from, $to);
233 if (!$success) {
234 throw new Exception(
235 'Creating link failed: from ' . $from . ' to: ' . $to,
236 1376657199
237 );
238 }
239 }
240 }
241
242 /**
243 * Link test extensions to the typo3conf/ext folder of the instance.
244 * For functional and acceptance tests.
245 *
246 * @param string $instancePath Absolute path to test instance
247 * @param array $extensionPaths Contains paths to extensions relative to document root
248 * @throws Exception
249 * @return void
250 */
251 public function linkTestExtensionsToInstance($instancePath, array $extensionPaths)
252 {
253 foreach ($extensionPaths as $extensionPath) {
254 $absoluteExtensionPath = ORIGINAL_ROOT . $extensionPath;
255 if (!is_dir($absoluteExtensionPath)) {
256 throw new Exception(
257 'Test extension path ' . $absoluteExtensionPath . ' not found',
258 1376745645
259 );
260 }
261 $destinationPath = $instancePath . '/typo3conf/ext/' . basename($absoluteExtensionPath);
262 $success = symlink($absoluteExtensionPath, $destinationPath);
263 if (!$success) {
264 throw new Exception(
265 'Can not link extension folder: ' . $absoluteExtensionPath . ' to ' . $destinationPath,
266 1376657142
267 );
268 }
269 }
270 }
271
272 /**
273 * Link paths inside the test instance, e.g. from a fixture fileadmin subfolder to the
274 * test instance fileadmin folder.
275 * For functional and acceptance tests.
276 *
277 * @param string $instancePath Absolute path to test instance
278 * @param array $pathsToLinkInTestInstance Contains paths as array of source => destination in key => value pairs of folders relative to test instance root
279 * @throws Exception if a source path could not be found and on failing creating the symlink
280 * @return void
281 */
282 public function linkPathsInTestInstance($instancePath, array $pathsToLinkInTestInstance)
283 {
284 foreach ($pathsToLinkInTestInstance as $sourcePathToLinkInTestInstance => $destinationPathToLinkInTestInstance) {
285 $sourcePath = $instancePath . '/' . ltrim($sourcePathToLinkInTestInstance, '/');
286 if (!file_exists($sourcePath)) {
287 throw new Exception(
288 'Path ' . $sourcePath . ' not found',
289 1476109221
290 );
291 }
292 $destinationPath = $instancePath . '/' . ltrim($destinationPathToLinkInTestInstance, '/');
293 $success = symlink($sourcePath, $destinationPath);
294 if (!$success) {
295 throw new Exception(
296 'Can not link the path ' . $sourcePath . ' to ' . $destinationPath,
297 1389969623
298 );
299 }
300 }
301 }
302
303 /**
304 * Database settings for functional and acceptance tests can be either set by
305 * environment variables (recommended), or from an existing LocalConfiguration as fallback.
306 * The method fetches these.
307 *
308 * An unique name will be added to the database name later.
309 *
310 * @throws Exception
311 * @return array [DB][host], [DB][username], ...
312 */
313 public function getOriginalDatabaseSettingsFromEnvironmentOrLocalConfiguration()
314 {
315 $databaseName = trim(getenv('typo3DatabaseName'));
316 $databaseHost = trim(getenv('typo3DatabaseHost'));
317 $databaseUsername = trim(getenv('typo3DatabaseUsername'));
318 $databasePassword = getenv('typo3DatabasePassword');
319 $databasePasswordTrimmed = trim($databasePassword);
320 $databasePort = trim(getenv('typo3DatabasePort'));
321 $databaseSocket = trim(getenv('typo3DatabaseSocket'));
322 $databaseDriver = trim(getenv('typo3DatabaseDriver'));
323 if ($databaseName || $databaseHost || $databaseUsername || $databasePassword || $databasePort || $databaseSocket) {
324 // Try to get database credentials from environment variables first
325 $originalConfigurationArray = [
326 'DB' => [
327 'Connections' => [
328 'Default' => [
329 'driver' => 'mysqli'
330 ],
331 ],
332 ],
333 ];
334 if ($databaseName) {
335 $originalConfigurationArray['DB']['Connections']['Default']['dbname'] = $databaseName;
336 }
337 if ($databaseHost) {
338 $originalConfigurationArray['DB']['Connections']['Default']['host'] = $databaseHost;
339 }
340 if ($databaseUsername) {
341 $originalConfigurationArray['DB']['Connections']['Default']['user'] = $databaseUsername;
342 }
343 if ($databasePassword !== false) {
344 $originalConfigurationArray['DB']['Connections']['Default']['password'] = $databasePasswordTrimmed;
345 }
346 if ($databasePort) {
347 $originalConfigurationArray['DB']['Connections']['Default']['port'] = $databasePort;
348 }
349 if ($databaseSocket) {
350 $originalConfigurationArray['DB']['Connections']['Default']['unix_socket'] = $databaseSocket;
351 }
352 if ($databaseDriver) {
353 $originalConfigurationArray['DB']['Connections']['Default']['driver'] = $databaseDriver;
354 }
355 } elseif (file_exists(ORIGINAL_ROOT . 'typo3conf/LocalConfiguration.php')) {
356 // See if a LocalConfiguration file exists in "parent" instance to get db credentials from
357 $originalConfigurationArray = require ORIGINAL_ROOT . 'typo3conf/LocalConfiguration.php';
358 } else {
359 throw new Exception(
360 'Database credentials for tests are neither set through environment'
361 . ' variables, and can not be found in an existing LocalConfiguration file',
362 1397406356
363 );
364 }
365 return $originalConfigurationArray['DB'];
366 }
367
368 /**
369 * Maximum length of database names is 64 chars in mysql. Test this is not exceeded
370 * after a suffix has been added.
371 *
372 * @param string $originalDatabaseName Base name of the database
373 * @param array $configuration "LocalConfiguration" array with DB settings
374 * @throws Exception
375 */
376 public function testDatabaseNameIsNotTooLong($originalDatabaseName, array $configuration)
377 {
378 // Maximum database name length for mysql is 64 characters
379 if (strlen($configuration['DB']['Connections']['Default']['dbname']) > 64) {
380 $suffixLength = strlen($configuration['DB']['Connections']['Default']['dbname']) - strlen($originalDatabaseName);
381 $maximumOriginalDatabaseName = 64 - $suffixLength;
382 throw new Exception(
383 'The name of the database that is used for the functional test (' . $originalDatabaseName . ')' .
384 ' exceeds the maximum length of 64 character allowed by MySQL. You have to shorten your' .
385 ' original database name to ' . $maximumOriginalDatabaseName . ' characters',
386 1377600104
387 );
388 }
389 }
390
391 /**
392 * Create LocalConfiguration.php file of the test instance.
393 * For functional and acceptance tests.
394 *
395 * @param string $instancePath Absolute path to test instance
396 * @param array $configuration Base configuration array
397 * @param array $overruleConfiguration Overrule factory and base configuration
398 * @throws Exception
399 * @return void
400 */
401 public function setUpLocalConfiguration($instancePath, array $configuration, array $overruleConfiguration)
402 {
403 // Base of final LocalConfiguration is core factory configuration
404 $finalConfigurationArray = require ORIGINAL_ROOT . 'typo3/sysext/core/Configuration/FactoryConfiguration.php';
405 ArrayUtility::mergeRecursiveWithOverrule($finalConfigurationArray, $configuration);
406 ArrayUtility::mergeRecursiveWithOverrule($finalConfigurationArray, $overruleConfiguration);
407 $result = $this->writeFile(
408 $instancePath . '/typo3conf/LocalConfiguration.php',
409 '<?php' . chr(10) .
410 'return ' .
411 ArrayUtility::arrayExport(
412 $finalConfigurationArray
413 ) .
414 ';'
415 );
416 if (!$result) {
417 throw new Exception('Can not write local configuration', 1376657277);
418 }
419 }
420
421 /**
422 * Compile typo3conf/PackageStates.php containing default packages like core,
423 * a test specific list of additional core extensions, and a list of
424 * test extensions.
425 * For functional and acceptance tests.
426 *
427 * @param string $instancePath Absolute path to test instance
428 * @param array $defaultCoreExtensionsToLoad Default list of core extensions to load
429 * @param array $additionalCoreExtensionsToLoad Additional core extensions to load
430 * @param array $testExtensionPaths Paths to extensions relative to document root
431 * @throws Exception
432 */
433 public function setUpPackageStates(
434 $instancePath,
435 array $defaultCoreExtensionsToLoad,
436 array $additionalCoreExtensionsToLoad,
437 array $testExtensionPaths
438 ) {
439 $packageStates = [
440 'packages' => [],
441 'version' => 5,
442 ];
443
444 // Register default list of extensions and set active
445 foreach ($defaultCoreExtensionsToLoad as $extensionName) {
446 $packageStates['packages'][$extensionName] = [
447 'packagePath' => 'typo3/sysext/' . $extensionName . '/'
448 ];
449 }
450
451 // Register additional core extensions and set active
452 foreach ($additionalCoreExtensionsToLoad as $extensionName) {
453 $packageStates['packages'][$extensionName] = [
454 'packagePath' => 'typo3/sysext/' . $extensionName . '/'
455 ];
456 }
457
458 // Activate test extensions that have been symlinked before
459 foreach ($testExtensionPaths as $extensionPath) {
460 $extensionName = basename($extensionPath);
461 $packageStates['packages'][$extensionName] = [
462 'packagePath' => 'typo3conf/ext/' . $extensionName . '/'
463 ];
464 }
465
466 $result = $this->writeFile(
467 $instancePath . '/typo3conf/PackageStates.php',
468 '<?php' . chr(10) .
469 'return ' .
470 ArrayUtility::arrayExport(
471 $packageStates
472 ) .
473 ';'
474 );
475
476 if (!$result) {
477 throw new Exception('Can not write PackageStates', 1381612729);
478 }
479 }
480
481 /**
482 * Create a low level connection to dbms, without selecting the target database.
483 * Drop existing database if it exists and create a new one.
484 *
485 * @param string $databaseName Database name of this test instance
486 * @param string $originalDatabaseName Original database name before suffix was added
487 * @throws \TYPO3\Components\TestingFramework\Core\Exception
488 * @return void
489 */
490 public function setUpTestDatabase($databaseName, $originalDatabaseName)
491 {
492 Bootstrap::getInstance()->initializeTypo3DbGlobal();
493
494 // Drop database if exists. Directly using the Doctrine DriverManager to
495 // work around connection caching in ConnectionPool
496 $connectionParameters = $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default'];
497 unset($connectionParameters['dbname']);
498 $schemaManager = DriverManager::getConnection($connectionParameters)->getSchemaManager();
499
500 if (in_array($databaseName, $schemaManager->listDatabases(), true)) {
501 $schemaManager->dropDatabase($databaseName);
502 }
503
504 try {
505 $schemaManager->createDatabase($databaseName);
506 } catch (DBALException $e) {
507 $user = $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['user'];
508 $host = $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['host'];
509 throw new Exception(
510 'Unable to create database with name ' . $databaseName . '. This is probably a permission problem.'
511 . ' For this instance this could be fixed executing:'
512 . ' GRANT ALL ON `' . $originalDatabaseName . '_%`.* TO `' . $user . '`@`' . $host . '`;'
513 . ' Original message thrown by database layer: ' . $e->getMessage(),
514 1376579070
515 );
516 }
517 }
518
519 /**
520 * Bootstrap basic TYPO3. This bootstraps TYPO3 far enough to initialize database afterwards.
521 * For functional and acceptance tests.
522 *
523 * @param string $instancePath Absolute path to test instance
524 * @return void
525 */
526 public function setUpBasicTypo3Bootstrap($instancePath)
527 {
528 $_SERVER['PWD'] = $instancePath;
529 $_SERVER['argv'][0] = 'index.php';
530
531 $classLoader = require rtrim(realpath($instancePath . '/typo3'), '\\/') . '/../vendor/autoload.php';
532 Bootstrap::getInstance()
533 ->initializeClassLoader($classLoader)
534 ->setRequestType(TYPO3_REQUESTTYPE_BE | TYPO3_REQUESTTYPE_CLI)
535 ->baseSetup()
536 ->loadConfigurationAndInitialize(true)
537 ->loadTypo3LoadedExtAndExtLocalconf(true)
538 ->setFinalCachingFrameworkCacheConfiguration()
539 ->defineLoggingAndExceptionConstants()
540 ->unsetReservedGlobalVariables();
541 }
542
543 /**
544 * Truncate all tables.
545 * For functional and acceptance tests.
546 *
547 * @throws Exception
548 * @return void
549 */
550 public function initializeTestDatabaseAndTruncateTables()
551 {
552 Bootstrap::getInstance()->initializeTypo3DbGlobal();
553
554 $connection = GeneralUtility::makeInstance(ConnectionPool::class)
555 ->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME);
556 $schemaManager = $connection->getSchemaManager();
557
558 foreach ($schemaManager->listTables() as $table) {
559 $connection->truncate($table->getName());
560 self::resetTableSequences($connection, $table->getName());
561 }
562 }
563
564 /**
565 * Load ext_tables.php files.
566 * For functional and acceptance tests.
567 *
568 * @return void
569 */
570 public function loadExtensionTables()
571 {
572 Bootstrap::getInstance()->loadExtensionTables();
573 }
574
575 /**
576 * Create tables and import static rows.
577 * For functional and acceptance tests.
578 *
579 * @return void
580 */
581 public function createDatabaseStructure()
582 {
583 $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
584 $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
585 $sqlCode = $sqlReader->getTablesDefinitionString(true);
586
587 $createTableStatements = $sqlReader->getCreateTableStatementArray($sqlCode);
588
589 $schemaMigrationService->install($createTableStatements);
590
591 $insertStatements = $sqlReader->getInsertStatementArray($sqlCode);
592 $schemaMigrationService->importStaticData($insertStatements);
593 }
594
595 /**
596 * Imports a data set represented as XML into the test database,
597 *
598 * @param string $path Absolute path to the XML file containing the data set to load
599 * @return void
600 * @throws \Doctrine\DBAL\DBALException
601 * @throws \InvalidArgumentException
602 * @throws \RuntimeException
603 */
604 public function importXmlDatabaseFixture($path)
605 {
606 if (!is_file($path)) {
607 throw new \RuntimeException(
608 'Fixture file ' . $path . ' not found',
609 1376746261
610 );
611 }
612
613 $fileContent = file_get_contents($path);
614 // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
615 $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
616 $xml = simplexml_load_string($fileContent);
617 libxml_disable_entity_loader($previousValueOfEntityLoader);
618 $foreignKeys = [];
619
620 /** @var $table \SimpleXMLElement */
621 foreach ($xml->children() as $table) {
622 $insertArray = [];
623
624 /** @var $column \SimpleXMLElement */
625 foreach ($table->children() as $column) {
626 $columnName = $column->getName();
627 $columnValue = null;
628
629 if (isset($column['ref'])) {
630 list($tableName, $elementId) = explode('#', $column['ref']);
631 $columnValue = $foreignKeys[$tableName][$elementId];
632 } elseif (isset($column['is-NULL']) && ($column['is-NULL'] === 'yes')) {
633 $columnValue = null;
634 } else {
635 $columnValue = (string)$table->$columnName;
636 }
637
638 $insertArray[$columnName] = $columnValue;
639 }
640
641 $tableName = $table->getName();
642 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName);
643 $connection->insert(
644 $tableName,
645 $insertArray
646 );
647 static::resetTableSequences($connection, $tableName);
648
649 if (isset($table['id'])) {
650 $elementId = (string)$table['id'];
651 $foreignKeys[$tableName][$elementId] = $connection->lastInsertId($tableName);
652 }
653 }
654 }
655
656 /**
657 * Perform post processing of database tables after an insert has been performed.
658 * Doing this once per insert is rather slow, but due to the soft reference behavior
659 * this needs to be done after every row to ensure consistent results.
660 *
661 * @param \TYPO3\CMS\Core\Database\Connection $connection
662 * @param string $tableName
663 * @throws \Doctrine\DBAL\DBALException
664 */
665 public static function resetTableSequences(Connection $connection, string $tableName)
666 {
667 if ($connection->getDatabasePlatform() instanceof PostgreSqlPlatform) {
668 $queryBuilder = $connection->createQueryBuilder();
669 $queryBuilder->getRestrictions()->removeAll();
670 $row = $queryBuilder->select('PGT.schemaname', 'S.relname', 'C.attname', 'T.relname AS tablename')
671 ->from('pg_class', 'S')
672 ->from('pg_depend', 'D')
673 ->from('pg_class', 'T')
674 ->from('pg_attribute', 'C')
675 ->from('pg_tables', 'PGT')
676 ->where(
677 $queryBuilder->expr()->eq('S.relkind', $queryBuilder->quote('S')),
678 $queryBuilder->expr()->eq('S.oid', $queryBuilder->quoteIdentifier('D.objid')),
679 $queryBuilder->expr()->eq('D.refobjid', $queryBuilder->quoteIdentifier('T.oid')),
680 $queryBuilder->expr()->eq('D.refobjid', $queryBuilder->quoteIdentifier('C.attrelid')),
681 $queryBuilder->expr()->eq('D.refobjsubid', $queryBuilder->quoteIdentifier('C.attnum')),
682 $queryBuilder->expr()->eq('T.relname', $queryBuilder->quoteIdentifier('PGT.tablename')),
683 $queryBuilder->expr()->eq('PGT.tablename', $queryBuilder->quote($tableName))
684 )
685 ->setMaxResults(1)
686 ->execute()
687 ->fetch();
688
689 if ($row !== false) {
690 $connection->exec(
691 sprintf(
692 'SELECT SETVAL(%s, COALESCE(MAX(%s), 0)+1, FALSE) FROM %s',
693 $connection->quote($row['schemaname'] . '.' . $row['relname']),
694 $connection->quoteIdentifier($row['attname']),
695 $connection->quoteIdentifier($row['schemaname'] . '.' . $row['tablename'])
696 )
697 );
698 }
699 }
700 }
701
702 /**
703 * Returns the absolute path the TYPO3 document root.
704 * This is the "original" document root, not the "instance" root for functional / acceptance tests.
705 *
706 * @return string the TYPO3 document root using Unix path separators
707 */
708 protected function getWebRoot()
709 {
710 if (getenv('TYPO3_PATH_WEB')) {
711 $webRoot = getenv('TYPO3_PATH_WEB');
712 } else {
713 $webRoot = getcwd();
714 }
715 return rtrim(strtr($webRoot, '\\', '/'), '/') . '/';
716 }
717
718 /**
719 * Send http headers, echo out a text message and exit with error code
720 *
721 * @param string $message
722 */
723 protected function exitWithMessage($message)
724 {
725 echo $message . LF;
726 exit(1);
727 }
728
729 /**
730 * Writes $content to the file $file. This is a simplified version
731 * of GeneralUtility::writeFile that does not fix permissions.
732 *
733 * @param string $file Filepath to write to
734 * @param string $content Content to write
735 * @return bool TRUE if the file was successfully opened and written to.
736 */
737 protected function writeFile($file, $content)
738 {
739 if ($fd = fopen($file, 'wb')) {
740 $res = fwrite($fd, $content);
741 fclose($fd);
742 if ($res === false) {
743 return false;
744 }
745 return true;
746 }
747 return false;
748 }
749 }