Index: tests/t3lib/cache/backend/t3lib_cache_backend_pdobackendtestcase.php
===================================================================
--- tests/t3lib/cache/backend/t3lib_cache_backend_pdobackendtestcase.php	(revision 0)
+++ tests/t3lib/cache/backend/t3lib_cache_backend_pdobackendtestcase.php	(revision 0)
@@ -0,0 +1,385 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2010 Christian Kuhn <lolli@schwarzbu.ch>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Testcase for the PDO cache backend
+ *
+ * @author	Christian Kuhn <lolli@schwarzbu.ch>
+ * @package TYPO3
+ * @subpackage tests
+ * @version $Id$
+ */
+class t3lib_cache_backend_PdoBackendTestCase extends tx_phpunit_testcase {
+
+	/**
+	 * Backup of global variable EXEC_TIME
+	 *
+	 * @var array
+	 */
+	protected $backupGlobalVariables;
+
+	/**
+	 * @var string
+	 */
+	protected $fixtureFolder;
+
+	/**
+	 * @var string
+	 */
+	protected $fixtureDB;
+
+	/**
+	 * Sets up this testcase
+	 *
+	 * @author Christian Kuhn <lolli@schwarzbu.ch>
+	 */
+	public function setUp() {
+		if (!extension_loaded('pdo_sqlite')) {
+			$this->markTestSkipped('pdo_sqlite extension was not available');
+		}
+
+		$this->backupGlobalVariables = array(
+			'EXEC_TIME' => $GLOBALS['EXEC_TIME'],
+		);
+	}
+
+	/**
+	 * @test
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @expectedException t3lib_cache_Exception
+	 */
+	public function setThrowsExceptionIfNoFrontEndHasBeenSet() {
+		$backend = t3lib_div::makeInstance('t3lib_cache_backend_PdoBackend');
+		$data = 'Some data';
+		$identifier = 'MyIdentifier';
+		$backend->set($identifier, $data);
+	}
+
+	/**
+	 * @test
+	 * @author Christian Jul Jensen <julle@typo3.org>
+	 */
+	public function itIsPossibleToSetAndCheckExistenceInCache() {
+		$backend = $this->setUpBackend();
+		$data = 'Some data';
+		$identifier = 'MyIdentifier';
+		$backend->set($identifier, $data);
+		$this->assertTrue($backend->has($identifier));
+	}
+
+	/**
+	 * @test
+	 * @author Christian Jul Jensen <julle@typo3.org>
+	 */
+	public function itIsPossibleToSetAndGetEntry() {
+		$backend = $this->setUpBackend();
+		$data = 'Some data';
+		$identifier = 'MyIdentifier';
+		$backend->set($identifier, $data);
+		$fetchedData = $backend->get($identifier);
+		$this->assertEquals($data, $fetchedData);
+	}
+
+	/**
+	 * @test
+	 * @author Christian Jul Jensen <julle@typo3.org>
+	 */
+	public function itIsPossibleToRemoveEntryFromCache() {
+		$backend = $this->setUpBackend();
+		$data = 'Some data';
+		$identifier = 'MyIdentifier';
+		$backend->set($identifier, $data);
+		$backend->remove($identifier);
+		$this->assertFalse($backend->has($identifier));
+	}
+
+	/**
+	 * @test
+	 * @author Christian Jul Jensen <julle@typo3.org>
+	 */
+	public function itIsPossibleToOverwriteAnEntryInTheCache() {
+		$backend = $this->setUpBackend();
+		$data = 'Some data';
+		$identifier = 'MyIdentifier';
+		$backend->set($identifier, $data);
+		$otherData = 'some other data';
+		$backend->set($identifier, $otherData);
+		$fetchedData = $backend->get($identifier);
+		$this->assertEquals($otherData, $fetchedData);
+	}
+
+	/**
+	 * @test
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	public function findIdentifiersByTagFindsSetEntries() {
+		$backend = $this->setUpBackend();
+
+		$data = 'Some data';
+		$entryIdentifier = 'MyIdentifier';
+		$backend->set($entryIdentifier, $data, array('UnitTestTag%tag1', 'UnitTestTag%tag2'));
+
+		$retrieved = $backend->findIdentifiersByTag('UnitTestTag%tag1');
+		$this->assertEquals($entryIdentifier, $retrieved[0]);
+
+		$retrieved = $backend->findIdentifiersByTag('UnitTestTag%tag2');
+		$this->assertEquals($entryIdentifier, $retrieved[0]);
+	}
+
+	/**
+	 * @test
+	 * @author Christian Kuhn <lolli@schwarzbu.ch>
+	 */
+	public function findIdentifiersByTagsFindsSetEntries() {
+		$backend = $this->setUpBackend();
+
+		$data = 'Some data';
+		$entryIdentifier = 'MyIdentifier';
+		$backend->set($entryIdentifier . 'A', $data, array('UnitTestTag%tag1'));
+		$backend->set($entryIdentifier . 'B', $data, array('UnitTestTag%tag2'));
+		$backend->set($entryIdentifier . 'C', $data, array('UnitTestTag%tag1', 'UnitTestTag%tag2'));
+		$backend->set($entryIdentifier . 'D', $data, array('UnitTestTag%tag1', 'UnitTestTag%tag2', 'UnitTestTag%tag3'));
+
+		$retrieved = $backend->findIdentifiersByTags(array('UnitTestTag%tag1', 'UnitTestTag%tag2'));
+
+		$this->assertFalse(in_array($entryIdentifier . 'A', $retrieved));
+		$this->assertFalse(in_array($entryIdentifier . 'B', $retrieved));
+		$this->assertTrue(in_array($entryIdentifier . 'C', $retrieved));
+		$this->assertTrue(in_array($entryIdentifier . 'D', $retrieved));
+	}
+
+	/**
+	 * @test
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	public function setRemovesTagsFromPreviousSet() {
+		$backend = $this->setUpBackend();
+
+		$data = 'Some data';
+		$entryIdentifier = 'MyIdentifier';
+		$backend->set($entryIdentifier, $data, array('UnitTestTag%tag1', 'UnitTestTag%tag2'));
+		$backend->set($entryIdentifier, $data, array('UnitTestTag%tag3'));
+
+		$retrieved = $backend->findIdentifiersByTag('UnitTestTag%tag2');
+		$this->assertEquals(array(), $retrieved);
+	}
+
+	/**
+	 * @test
+	 * @author Christian Jul Jensen <julle@typo3.org>
+	 */
+	public function hasReturnsFalseIfTheEntryDoesntExist() {
+		$backend = $this->setUpBackend();
+		$identifier = 'NonExistingIdentifier';
+		$this->assertFalse($backend->has($identifier));
+	}
+
+	/**
+	 * @test
+	 * @author Christian Jul Jensen <julle@typo3.org>
+	 */
+	public function removeReturnsFalseIfTheEntryDoesntExist() {
+		$backend = $this->setUpBackend();
+		$identifier = 'NonExistingIdentifier';
+		$this->assertFalse($backend->remove($identifier));
+	}
+
+	/**
+	 * @test
+	 * @author Robert Lemke <robert@typo3.org>
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	public function flushByTagRemovesCacheEntriesWithSpecifiedTag() {
+		$backend = $this->setUpBackend();
+
+		$data = 'some data' . microtime();
+		$backend->set('PdoBackendTest1', $data, array('UnitTestTag%test', 'UnitTestTag%boring'));
+		$backend->set('PdoBackendTest2', $data, array('UnitTestTag%test', 'UnitTestTag%special'));
+		$backend->set('PdoBackendTest3', $data, array('UnitTestTag%test'));
+
+		$backend->flushByTag('UnitTestTag%special');
+
+		$this->assertTrue($backend->has('PdoBackendTest1'), 'PdoBackendTest1');
+		$this->assertFalse($backend->has('PdoBackendTest2'), 'PdoBackendTest2');
+		$this->assertTrue($backend->has('PdoBackendTest3'), 'PdoBackendTest3');
+	}
+
+	/**
+	 * @test
+	 * @author Christian Kuhn <lolli@schwarzbu.ch>
+	 */
+	public function flushByTagsRemovesCacheEntriesWithSpecifiedTags() {
+		$backend = $this->setUpBackend();
+
+		$data = 'some data' . microtime();
+		$backend->set('PdoBackendTest1', $data, array('UnitTestTag%test', 'UnitTestTag%boring'));
+		$backend->set('PdoBackendTest2', $data, array('UnitTestTag%test', 'UnitTestTag%special1'));
+		$backend->set('PdoBackendTest3', $data, array('UnitTestTag%test', 'UnitTestTag%special2'));
+		$backend->set('PdoBackendTest4', $data, array('UnitTestTag%test', 'UnitTestTag%special2'));
+
+		$backend->flushByTags(array('UnitTestTag%special1','UnitTestTag%special2'));
+
+		$this->assertTrue($backend->has('PdoBackendTest1'));
+		$this->assertFalse($backend->has('PdoBackendTest2'));
+		$this->assertFalse($backend->has('PdoBackendTest3'));
+		$this->assertFalse($backend->has('PdoBackendTest4'));
+	}
+
+	/**
+	 * @test
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	public function flushRemovesAllCacheEntries() {
+		$backend = $this->setUpBackend();
+
+		$data = 'some data' . microtime();
+		$backend->set('PdoBackendTest1', $data);
+		$backend->set('PdoBackendTest2', $data);
+		$backend->set('PdoBackendTest3', $data);
+
+		$backend->flush();
+
+		$this->assertFalse($backend->has('PdoBackendTest1'), 'PdoBackendTest1');
+		$this->assertFalse($backend->has('PdoBackendTest2'), 'PdoBackendTest2');
+		$this->assertFalse($backend->has('PdoBackendTest3'), 'PdoBackendTest3');
+	}
+
+	/**
+	 * @test
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	public function flushRemovesOnlyOwnEntries() {
+		$thisCache = $this->getMock('t3lib_cache_frontend_Frontend', array(), array(), '', FALSE);
+		$thisCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('thisCache'));
+		$thisBackend = $this->setUpBackend();
+		$thisBackend->setCache($thisCache);
+
+		$thatCache = $this->getMock('t3lib_cache_frontend_Frontend', array(), array(), '', FALSE);
+		$thatCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('thatCache'));
+		$thatBackend = $this->setUpBackend();
+		$thatBackend->setCache($thatCache);
+
+		$thisBackend->set('thisEntry', 'Hello');
+		$thatBackend->set('thatEntry', 'World!');
+		$thatBackend->flush();
+
+		$this->assertEquals('Hello', $thisBackend->get('thisEntry'));
+		$this->assertFalse($thatBackend->has('thatEntry'));
+	}
+
+	/**
+	 * @test
+	 * @author Ingo Renner <ingo@typo3.org>
+	 * @author Christian Kuhn <lolli@schwarzbu.ch>
+	 */
+	public function collectGarbageReallyRemovesAnExpiredCacheEntry() {
+		$backend = $this->setUpBackend();
+
+		$data = 'some data' . microtime();
+		$entryIdentifier = 'BackendPDORemovalTest';
+		$backend->set($entryIdentifier, $data, array(), 1);
+
+		$this->assertTrue($backend->has($entryIdentifier));
+
+		$GLOBALS['EXEC_TIME'] += 2;
+		$backend->collectGarbage();
+
+		$this->assertFalse($backend->has($entryIdentifier));
+	}
+
+	/**
+	 * @test
+	 * @author Ingo Renner <ingo@typo3.org>
+	 * @author Christian Kuhn <lolli@schwarzbu.ch>
+	 */
+	public function collectGarbageReallyRemovesAllExpiredCacheEntries() {
+		$backend = $this->setUpBackend();
+
+		$data = 'some data' . microtime();
+		$entryIdentifier = 'BackendPDORemovalTest';
+
+		$backend->set($entryIdentifier . 'A', $data, array(), NULL);
+		$backend->set($entryIdentifier . 'B', $data, array(), 10);
+		$backend->set($entryIdentifier . 'C', $data, array(), 1);
+		$backend->set($entryIdentifier . 'D', $data, array(), 1);
+
+		$this->assertTrue($backend->has($entryIdentifier . 'A'));
+		$this->assertTrue($backend->has($entryIdentifier . 'B'));
+		$this->assertTrue($backend->has($entryIdentifier . 'C'));
+		$this->assertTrue($backend->has($entryIdentifier . 'D'));
+
+		$GLOBALS['EXEC_TIME'] += 2;
+		$backend->collectGarbage();
+
+		$this->assertTrue($backend->has($entryIdentifier . 'A'));
+		$this->assertTrue($backend->has($entryIdentifier . 'B'));
+		$this->assertFalse($backend->has($entryIdentifier . 'C'));
+		$this->assertFalse($backend->has($entryIdentifier . 'D'));
+	}
+
+	/**
+	 * Sets up the PDO backend used for testing
+	 *
+	 * @return t3lib_cache_backend_PdoBackend
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	protected function setUpBackend() {
+		$this->fixtureFolder = sys_get_temp_dir() . '/' . 'typo3pdobackendtest/';
+		t3lib_div::mkdir_deep(sys_get_temp_dir() . '/', 'typo3pdobackendtest/');
+		$this->fixtureDB = uniqid('Cache') . '.db';
+
+		$pdoHelper = t3lib_div::makeInstance('t3lib_PdoHelper', 'sqlite:' . $this->fixtureFolder . $this->fixtureDB, '', '');
+		$pdoHelper->importSql(PATH_t3lib . 'cache/backend/resources/ddl.sql');
+
+		$mockCache = $this->getMock('t3lib_cache_frontend_Frontend', array(), array(), '', FALSE);
+		$mockCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('TestCache'));
+
+		$backendOptions = array(
+			'dataSourceName' => 'sqlite:' . $this->fixtureFolder . $this->fixtureDB,
+			'username' => '',
+			'password' => '',
+		);
+		$backend = t3lib_div::makeInstance('t3lib_cache_backend_PdoBackend', $backendOptions);
+		$backend->setCache($mockCache);
+
+		return $backend;
+	}
+
+	/**
+	 * Clean up after the tests
+	 *
+	 * @return void
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	public function tearDown() {
+		if ($this->fixtureDB) {
+			t3lib_div::rmdir($this->fixtureFolder, TRUE);
+		}
+		foreach ($this->backupGlobalVariables as $key => $data) {
+			$GLOBALS[$key] = $data;
+		}
+	}
+}
+
+?>
Index: t3lib/config_default.php
===================================================================
--- t3lib/config_default.php	(revision 7829)
+++ t3lib/config_default.php	(working copy)
@@ -117,6 +117,7 @@
 				't3lib_cache_backend_FileBackend'      => 't3lib/cache/backend/class.t3lib_cache_backend_filebackend.php:t3lib_cache_backend_FileBackend',
 				't3lib_cache_backend_GlobalsBackend'   => 't3lib/cache/backend/class.t3lib_cache_backend_globalsbackend.php:t3lib_cache_backend_GlobalsBackend',
 				't3lib_cache_backend_MemcachedBackend' => 't3lib/cache/backend/class.t3lib_cache_backend_memcachedbackend.php:t3lib_cache_backend_MemcachedBackend',
+				't3lib_cache_backend_PdoBackend'       => 't3lib/cache/backend/class.t3lib_cache_backend_pdobackend.php:t3lib_cache_backend_PdoBackend',
 				't3lib_cache_backend_ApcBackend'       => 't3lib/cache/backend/class.t3lib_cache_backend_apcbackend.php:t3lib_cache_backend_ApcBackend',
 				't3lib_cache_backend_NullBackend'      => 't3lib/cache/backend/class.t3lib_cache_backend_nullbackend.php:t3lib_cache_backend_NullBackend'
 			),
Index: t3lib/cache/backend/resources/ddl.sql
===================================================================
--- t3lib/cache/backend/resources/ddl.sql	(revision 0)
+++ t3lib/cache/backend/resources/ddl.sql	(revision 0)
@@ -0,0 +1,22 @@
+BEGIN;
+
+CREATE TABLE "cache" (
+  "identifier" VARCHAR(250) NOT NULL,
+  "cache" VARCHAR(250) NOT NULL,
+  "scope" CHAR(12) NOT NULL,
+  "created" INTEGER UNSIGNED NOT NULL,
+  "lifetime" INTEGER UNSIGNED DEFAULT '0' NOT NULL,
+  "content" TEXT,
+  PRIMARY KEY ("identifier", "cache", "scope")
+);
+
+CREATE TABLE "tags" (
+  "identifier" VARCHAR(250) NOT NULL,
+  "cache" VARCHAR(250) NOT NULL,
+  "scope" CHAR(12) NOT NULL,
+  "tag" VARCHAR(250) NOT NULL
+);
+CREATE INDEX "identifier" ON "tags" ("identifier", "cache", "scope");
+CREATE INDEX "tag" ON "tags" ("tag");
+
+COMMIT;
Index: t3lib/cache/backend/class.t3lib_cache_backend_pdobackend.php
===================================================================
--- t3lib/cache/backend/class.t3lib_cache_backend_pdobackend.php	(revision 0)
+++ t3lib/cache/backend/class.t3lib_cache_backend_pdobackend.php	(revision 0)
@@ -0,0 +1,451 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2010 Christian Kuhn <lolli@schwarzbu.ch>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * A PDO database cache backend
+ *
+ * @package TYPO3
+ * @subpackage t3lib_cache
+ * @api
+ * @scope prototype
+ * @author Christian Kuhn <lolli@schwarzbu.ch>
+ * @version $Id$
+ */
+class t3lib_cache_backend_PdoBackend extends t3lib_cache_backend_AbstractBackend {
+	/**
+	 * @var string
+	 */
+	protected $dataSourceName;
+
+	/**
+	 * @var string
+	 */
+	protected $username;
+
+	/**
+	 * @var string
+	 */
+	protected $password;
+
+	/**
+	 * Used to seperate stored data by user, SAPI, context, ...
+	 * @var string
+	 */
+	protected $scope;
+
+	/**
+	 * @var PDO
+	 */
+	protected $databaseHandle;
+
+	/**
+	 * @var string
+	 */
+	protected $pdoDriver;
+
+	/**
+	 * Constructs this backend
+	 *
+	 * @param mixed $options Configuration options - depends on the actual backend
+	 * @author Christian Kuhn <lolli@schwarzbu.ch>
+	 */
+	public function __construct(array $options = array()) {
+		parent::__construct($options);
+
+		$this->connect();
+	}
+
+	/**
+	 * Sets the DSN to use
+	 *
+	 * @param string $DSN The DSN to use for connecting to the DB
+	 * @return void
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function setDataSourceName($DSN) {
+		$this->dataSourceName = $DSN;
+	}
+
+	/**
+	 * Sets the username to use
+	 *
+	 * @param string $username The username to use for connecting to the DB
+	 * @return void
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function setUsername($username) {
+		$this->username = $username;
+	}
+
+	/**
+	 * Sets the password to use
+	 *
+	 * @param string $password The password to use for connecting to the DB
+	 * @return void
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function setPassword($password) {
+		$this->password = $password;
+	}
+
+	/**
+	 * Initializes the identifier prefix when setting the cache.
+	 *
+	 * @param t3lib_cache_frontend_Frontend $cache
+	 * @return void
+	 * @author Robert Lemke <robert@typo3.org>
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	public function setCache(t3lib_cache_frontend_Frontend $cache) {
+		parent::setCache($cache);
+		$processUser = extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : array('name' => 'default');
+		$this->scope = t3lib_div::shortMD5(PATH_site . $processUser['name'], 12);
+	}
+
+	/**
+	 * Saves data in the cache.
+	 *
+	 * @param string $entryIdentifier An identifier for this specific cache entry
+	 * @param string $data The data to be stored
+	 * @param array $tags Tags to associate with this cache entry
+	 * @param integer $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited liftime.
+	 * @return void
+	 * @throws t3lib_cache_Exception if no cache frontend has been set.
+	 * @throws t3lib_cache_exception_InvalidData if $data is not a string
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) {
+		if (!$this->cache instanceof t3lib_cache_frontend_Frontend) {
+			throw new t3lib_cache_Exception(
+				'No cache frontend has been set yet via setCache().',
+				1259515600
+			);
+		}
+
+		if (!is_string($data)) {
+			throw new t3lib_cache_exception_InvalidData(
+				'The specified data is of type "' . gettype($data) . '" but a string is expected.',
+				1259515601
+			);
+		}
+
+		if ($this->has($entryIdentifier)) {
+			$this->remove($entryIdentifier);
+		}
+
+		$lifetime = ($lifetime === NULL) ? $this->defaultLifetime : $lifetime;
+
+		$statementHandle = $this->databaseHandle->prepare(
+			'INSERT INTO "cache" ("identifier", "scope", "cache", "created", "lifetime", "content") VALUES (?, ?, ?, ?, ?, ?)'
+		);
+		$result = $statementHandle->execute(
+			array($entryIdentifier, $this->scope, $this->cacheIdentifier, $GLOBALS['EXEC_TIME'], $lifetime, $data)
+		);
+
+		if ($result === FALSE) {
+			throw new t3lib_cache_Exception(
+				'The cache entry "' . $entryIdentifier . '" could not be written.',
+				1259530791
+			);
+		}
+
+		$statementHandle = $this->databaseHandle->prepare(
+			'INSERT INTO "tags" ("identifier", "scope", "cache", "tag") VALUES (?, ?, ?, ?)'
+		);
+
+		foreach ($tags as $tag) {
+			$result = $statementHandle->execute(
+				array($entryIdentifier, $this->scope, $this->cacheIdentifier, $tag)
+			);
+			if ($result === FALSE) {
+				throw new t3lib_cache_Exception(
+					'The tag "' . $tag . ' for cache entry "' . $entryIdentifier . '" could not be written.',
+					1259530751
+				);
+			}
+		}
+	}
+
+	/**
+	 * Loads data from the cache.
+	 *
+	 * @param string $entryIdentifier An identifier which describes the cache entry to load
+	 * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function get($entryIdentifier) {
+		$statementHandle = $this->databaseHandle->prepare(
+			'SELECT "content" FROM "cache" WHERE "identifier"=? AND "scope"=? AND "cache"=?' . $this->getNotExpiredStatement()
+		);
+		$statementHandle->execute(
+			array($entryIdentifier, $this->scope, $this->cacheIdentifier)
+		);
+		return $statementHandle->fetchColumn();
+	}
+
+	/**
+	 * Checks if a cache entry with the specified identifier exists.
+	 *
+	 * @param string $entryIdentifier An identifier specifying the cache entry
+	 * @return boolean TRUE if such an entry exists, FALSE if not
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function has($entryIdentifier) {
+		$statementHandle = $this->databaseHandle->prepare(
+			'SELECT COUNT("identifier") FROM "cache" WHERE "identifier"=? AND "scope"=? AND "cache"=?' . $this->getNotExpiredStatement()
+		);
+		$statementHandle->execute(
+			array($entryIdentifier, $this->scope, $this->cacheIdentifier)
+		);
+		return ($statementHandle->fetchColumn() > 0);
+	}
+
+	/**
+	 * Removes all cache entries matching the specified identifier.
+	 * Usually this only affects one entry but if - for what reason ever -
+	 * old entries for the identifier still exist, they are removed as well.
+	 *
+	 * @param string $entryIdentifier Specifies the cache entry to remove
+	 * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function remove($entryIdentifier) {
+		$statementHandle = $this->databaseHandle->prepare(
+			'DELETE FROM "tags" WHERE "identifier"=? AND "scope"=? AND "cache"=?'
+		);
+		$statementHandle->execute(
+			array($entryIdentifier, $this->scope, $this->cacheIdentifier)
+		);
+
+		$statementHandle = $this->databaseHandle->prepare(
+			'DELETE FROM "cache" WHERE "identifier"=? AND "scope"=? AND "cache"=?'
+		);
+		$statementHandle->execute(
+			array($entryIdentifier, $this->scope, $this->cacheIdentifier)
+		);
+
+		return ($statementHandle->rowCount() > 0);
+	}
+
+	/**
+	 * Removes all cache entries of this cache.
+	 *
+	 * @return void
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function flush() {
+		$statementHandle = $this->databaseHandle->prepare(
+			'DELETE FROM "tags" WHERE "scope"=? AND "cache"=?'
+		);
+		$statementHandle->execute(
+			array($this->scope, $this->cacheIdentifier)
+		);
+
+		$statementHandle = $this->databaseHandle->prepare(
+			'DELETE FROM "cache" WHERE "scope"=? AND "cache"=?'
+		);
+		$statementHandle->execute(
+			array($this->scope, $this->cacheIdentifier)
+		);
+	}
+
+	/**
+	 * Removes all cache entries of this cache which are tagged by the specified tag.
+	 *
+	 * @param string $tag The tag the entries must have
+	 * @return void
+	 * @author Robert Lemke <robert@typo3.org>
+	 * @api
+	 */
+	public function flushByTag($tag) {
+		$statementHandle = $this->databaseHandle->prepare(
+			'DELETE FROM "cache" WHERE "scope"=? AND "cache"=? AND "identifier" IN (SELECT "identifier" FROM "tags" WHERE "scope"=? AND "cache"=? AND "tag"=?)'
+		);
+		$statementHandle->execute(
+			array($this->scope, $this->cacheIdentifier,$this->scope, $this->cacheIdentifier, $tag)
+		);
+
+		$statementHandle = $this->databaseHandle->prepare(
+			'DELETE FROM "tags" WHERE "scope"=? AND "cache"=? AND "tag"=?'
+		);
+		$statementHandle->execute(
+			array($this->scope, $this->cacheIdentifier, $tag)
+		);
+	}
+
+	/**
+	 * Removes all cache entries of this cache which are tagged by the specified tags.
+	 * This method doesn't exist in FLOW3, but is mandatory for TYPO3v4.
+	 *
+	 * @TODO: Make smarter
+	 * @param array $tags The tags the entries must have
+	 * @return void
+	 * @author Christian Kuhn <lolli@schwarzbu.ch>
+	 */
+	public function flushBytags(array $tags) {
+		foreach($tags as $tag) {
+			$this->flushByTag($tag);
+		}
+	}
+
+	/**
+	 * Finds and returns all cache entry identifiers which are tagged by the
+	 * specified tag.
+	 *
+	 * @param string $tag The tag to search for
+	 * @return array An array with identifiers of all matching entries. An empty array if no entries matched
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function findIdentifiersByTag($tag) {
+		$statementHandle = $this->databaseHandle->prepare(
+			'SELECT "identifier" FROM "tags" WHERE "scope"=?  AND "cache"=? AND "tag"=?'
+		);
+		$statementHandle->execute(
+			array($this->scope, $this->cacheIdentifier, $tag)
+		);
+		return $statementHandle->fetchAll(PDO::FETCH_COLUMN);
+	}
+
+	/**
+	 * Finds and returns all cache entry identifiers which are tagged with
+	 * all of the specified tags.
+	 * This method doesn't exist in FLOW3, but is mandatory for TYPO3v4.
+	 *
+	 * @TODO: Make smarter
+	 * @param array $tags Tags to search for
+	 * @return array An array with identifiers of all matching entries. An empty array if no entries matched
+	 * @author Christian Kuhn <lolli@schwarzbu.ch>
+	 */
+	public function findIdentifiersByTags(array $tags) {
+		$taggedEntries = array();
+		$foundEntries  = array();
+
+		foreach ($tags as $tag) {
+			$taggedEntries[$tag] = $this->findIdentifiersByTag($tag);
+		}
+
+		$intersectedTaggedEntries = call_user_func_array('array_intersect', $taggedEntries);
+
+		foreach ($intersectedTaggedEntries as $entryIdentifier) {
+			if ($this->has($entryIdentifier)) {
+				$foundEntries[$entryIdentifier] = $entryIdentifier;
+			}
+		}
+
+		return $foundEntries;
+	}
+
+	/**
+	 * Does garbage collection
+	 *
+	 * @return void
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 * @api
+	 */
+	public function collectGarbage() {
+		$statementHandle = $this->databaseHandle->prepare(
+			'DELETE FROM "tags" WHERE "scope"=? AND "cache"=? AND "identifier" IN ' .
+				'(SELECT "identifier" FROM "cache" WHERE "scope"=? AND "cache"=? AND "lifetime" > 0 AND "created" + "lifetime" < ' . $GLOBALS['EXEC_TIME'] . ')'
+		);
+		$statementHandle->execute(
+			array($this->scope, $this->cacheIdentifier, $this->scope, $this->cacheIdentifier)
+		);
+
+		$statementHandle = $this->databaseHandle->prepare(
+			'DELETE FROM "cache" WHERE "scope"=? AND "cache"=? AND "lifetime" > 0 AND "created" + "lifetime" < ' . $GLOBALS['EXEC_TIME']
+		);
+		$statementHandle->execute(
+			array($this->scope, $this->cacheIdentifier)
+		);
+	}
+
+	/**
+	 * Returns an SQL statement that evaluates to true if the entry is not expired.
+	 *
+	 * @return string
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	protected function getNotExpiredStatement() {
+		return ' AND ("lifetime" = 0 OR "created" + "lifetime" >= ' . $GLOBALS['EXEC_TIME'] . ')';
+	}
+
+	/**
+	 * Connect to the database
+	 *
+	 * @return void
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	protected function connect() {
+		try {
+			$splitdsn = explode(':', $this->dataSourceName, 2);
+			$this->pdoDriver = $splitdsn[0];
+
+			if ($this->pdoDriver === 'sqlite' && !file_exists($splitdsn[1])) {
+				$this->createCacheTables();
+			}
+
+			$this->databaseHandle = t3lib_div::makeInstance('PDO', $this->dataSourceName, $this->username, $this->password);
+			$this->databaseHandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+			if ($this->pdoDriver === 'mysql') {
+				$this->databaseHandle->exec('SET SESSION sql_mode=\'ANSI\';');
+			}
+		} catch (PDOException $e) {
+#			$this->createCacheTables();
+		}
+	}
+
+	/**
+	 * Creates the tables needed for the cache backend.
+	 *
+	 * @return void
+	 * @throws RuntimeException if something goes wrong
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	protected function createCacheTables() {
+		try {
+			$pdoHelper = t3lib_div::makeInstance('t3lib_PdoHelper', $this->dataSourceName, $this->username, $this->password);
+			$pdoHelper->importSql(PATH_t3lib . 'cache/backend/resources/ddl.sql');
+		} catch (PDOException $e) {
+			throw new RuntimeException(
+				'Could not create cache tables with DSN "' . $this->dataSourceName . '". PDO error: ' . $e->getMessage(),
+				1259576985
+			);
+		}
+	}
+}
+
+if (defined('TYPO3_MODE') && $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_pdobackend.php']) {
+	include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_pdobackend.php']);
+}
+?>
Index: t3lib/core_autoload.php
===================================================================
--- t3lib/core_autoload.php	(revision 7829)
+++ t3lib/core_autoload.php	(working copy)
@@ -91,6 +91,7 @@
 	't3lib_cache_backend_filebackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_filebackend.php',
 	't3lib_cache_backend_memcachedbackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_memcachedbackend.php',
 	't3lib_cache_backend_nullbackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_nullbackend.php',
+	't3lib_cache_backend_pdobackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_pdobackend.php',
 	't3lib_cache_backend_transientmemorybackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_transientmemorybackend.php',
 	't3lib_cache_backend_backend' => PATH_t3lib . 'cache/backend/interfaces/interface.t3lib_cache_backend_backend.php',
 	't3lib_cache_exception_classalreadyloaded' => PATH_t3lib . 'cache/exception/class.t3lib_cache_exception_classalreadyloaded.php',
@@ -121,6 +122,7 @@
 	't3lib_matchcondition_abstract' => PATH_t3lib . 'matchcondition/class.t3lib_matchcondition_abstract.php',
 	't3lib_matchcondition_backend' => PATH_t3lib . 'matchcondition/class.t3lib_matchcondition_backend.php',
 	't3lib_matchcondition_frontend' => PATH_t3lib . 'matchcondition/class.t3lib_matchcondition_frontend.php',
+	't3lib_pdohelper' => PATH_t3lib . 'class.t3lib_pdohelper.php',
 	't3lib_tceforms_suggest' => PATH_t3lib . 'tceforms/class.t3lib_tceforms_suggest.php',
 	't3lib_tceforms_suggest_defaultreceiver' => PATH_t3lib . 'tceforms/class.t3lib_tceforms_suggest_defaultreceiver.php',
 	't3lib_utility_client' => PATH_t3lib . 'utility/class.t3lib_utility_client.php',
Index: t3lib/class.t3lib_pdohelper.php
===================================================================
--- t3lib/class.t3lib_pdohelper.php	(revision 0)
+++ t3lib/class.t3lib_pdohelper.php	(revision 0)
@@ -0,0 +1,103 @@
+<?php
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2010 Christian Kuhn <lolli@schwarzbu.ch>
+ * All rights reserved
+ *
+ * This script is part of the TYPO3 project. The TYPO3 project is
+ * free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The GNU General Public License can be found at
+ * http://www.gnu.org/copyleft/gpl.html.
+ * A copy is found in the textfile GPL.txt and important notices to the license
+ * from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ * This script is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * A helper class for handling PDO databases
+ * Backport of FLOW3 class PdoHelper, last synced version: 3528
+ *
+ * @author Karsten Dambekalns <karsten@typo3.org>
+ * @package TYPO3
+ * @subpackage t3lib
+ * @version $Id$
+ * @scope prototype
+ */
+class t3lib_PdoHelper {
+
+	/**
+	 * @var PDO
+	 */
+	protected $databaseHandle;
+
+	/**
+	 * @var string
+	 */
+	protected $pdoDriver;
+
+	/**
+	 * Construct the helper instance and set up PDO connection.
+	 *
+	 * @param string $dataSourceName
+	 * @param string $user
+	 * @param string $password
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	public function __construct($dataSourceName, $user, $password) {
+		$splitdsn = explode(':', $dataSourceName, 2);
+		$this->pdoDriver = $splitdsn[0];
+
+		$this->databaseHandle = t3lib_div::makeInstance('PDO', $dataSourceName, $user, $password);
+		$this->databaseHandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+		if ($this->pdoDriver === 'mysql') {
+			$this->databaseHandle->exec('SET SESSION sql_mode=\'ANSI_QUOTES\';');
+		}
+	}
+
+	/**
+	 * Pumps the SQL into the database. Use for DDL only.
+	 *
+	 * Important: key definitions with length specifiers (needed for MySQL) must
+	 * be given as "field"(xyz) - no space between double quote and parenthesis -
+	 * so they can be removed automatically.
+	 *
+	 * @param string $pathAndFilename
+	 * @return void
+	 * @author Karsten Dambekalns <karsten@typo3.org>
+	 */
+	public function importSql($pathAndFilename) {
+		$sql = file($pathAndFilename, FILE_IGNORE_NEW_LINES & FILE_SKIP_EMPTY_LINES);
+
+			// Remove MySQL style key length delimiters (yuck!) if we are not setting up a MySQL db
+		if ($this->pdoDriver !== 'mysql') {
+			$sql = preg_replace('/"\([0-9]+\)/', '"', $sql);
+		}
+
+		$statement = '';
+		foreach ($sql as $line) {
+			$statement .= ' ' . trim($line);
+			if (substr($statement, -1) === ';') {
+				$this->databaseHandle->query($statement);
+				$statement = '';
+			}
+		}
+	}
+}
+
+if (defined('TYPO3_MODE') && $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_pdohelper.php']) {
+	include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_pdohelper.php']);
+}
+
+?>
