diff --git a/bin/importPdoMetadata.php b/bin/importPdoMetadata.php
new file mode 100755
index 0000000000000000000000000000000000000000..d09dd708a9c166d98985ec1800ee66ba49afc5da
--- /dev/null
+++ b/bin/importPdoMetadata.php
@@ -0,0 +1,27 @@
+<?php
+$baseDir = dirname(dirname(__FILE__));
+
+require_once $baseDir . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . '_autoload.php';
+require_once $baseDir . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php';
+
+# Iterate through configured metadata sources and ensure
+# that a PDO source exists.
+foreach ($config['metadata.sources'] as $s) {
+	# If pdo is configured, create the new handler and add in the metadata sets.
+	if ($s['type'] === "pdo") {
+		$mdshp = new SimpleSAML_Metadata_MetaDataStorageHandlerPdo($s);
+		$mdshp->initDatabase();
+
+		foreach (glob("metadata/*.php") as $filename) {
+			$metadata = array();
+			require_once $filename;
+			$set = basename($filename, ".php");
+			echo "importing set '$set'..." . PHP_EOL;
+
+			foreach ($metadata as $k => $v) {
+				echo "\t$k" . PHP_EOL;
+				$mdshp->addEntry($k, $set, $v);
+			}
+		}
+	}
+}
diff --git a/bin/initMDSPdo.php b/bin/initMDSPdo.php
new file mode 100755
index 0000000000000000000000000000000000000000..ff6d08e71cd50216805e29fcf918282c573af1d4
--- /dev/null
+++ b/bin/initMDSPdo.php
@@ -0,0 +1,27 @@
+#!/usr/bin/env php
+<?php
+
+/* This is the base directory of the simpleSAMLphp installation. */
+$baseDir = dirname(dirname(__FILE__));
+
+/* Add library autoloader and configuration. */
+require_once $baseDir . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . '_autoload.php';
+require_once $baseDir . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php';
+
+echo "Initializing Metadata Database...". PHP_EOL;
+
+# Iterate through configured metadata sources and ensure
+# that a PDO source exists.
+foreach ($config['metadata.sources'] as $source) {
+	# If pdo is configured, create the new handler and initialize the DB.
+	if ($source['type'] === "pdo") {
+		$metadataStorageHandler = new SimpleSAML_Metadata_MetaDataStorageHandlerPdo($source);
+		$result = $metadataStorageHandler->initDatabase();
+
+		if ($result === false) {
+			echo "Failed to intialize metadata database.". PHP_EOL;
+		} else {
+			echo "Successfully initialized metadata database.". PHP_EOL;
+		}
+	}
+}
diff --git a/config-templates/config.php b/config-templates/config.php
index acfef7223ad3991f28bf40abacd0382b696033c6..7a8db7ff23f2c676950249d0c53633bfd48e5795 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -197,6 +197,61 @@ $config = array(
     ),
 
 
+
+    /*
+     * Database
+     *
+     * This database configuration is optional. If you are not using
+     * core functionality or modules that require a database, you can
+     * skip this configuration.
+     */
+
+    /*
+     * Database connection string.
+     * Ensure that you have the required PDO database driver installed
+     * for your connection string.
+     */
+    'database.dsn' => 'mysql:host=localhost;dbname=saml',
+
+    /*
+     * SQL database credentials
+     */
+    'database.username' => 'simplesamlphp',
+    'database.password' => 'secret',
+
+    /*
+     * (Optional) Table prefix
+     */
+    'database.prefix' => '',
+
+    /*
+     * True or false if you would like a persistent database connection
+     */
+    'database.persistent' => false,
+
+    /*
+     * Database slave configuration is optional as well. If you are only
+     * running a single database server, leave this blank. If you have
+     * a master/slave configuration, you can define as many slave servers
+     * as you want here. Slaves will be picked at random to be queried from.
+     *
+     * Configuration options in the slave array are exactly the same as the
+     * options for the master (shown above) with the exception of the table
+     * prefix.
+     */
+    'database.slaves' => array(
+        /*
+        array(
+            'dsn' => 'mysql:host=myslave;dbname=saml',
+            'username' => 'simplesamlphp',
+            'password' => 'secret',
+            'persistent' => false,
+        ),
+        */
+    ),
+
+
+
     /*
      * Enable
      *
@@ -571,6 +626,15 @@ $config = array(
      * - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
      *                  hours (86400 seconds). Optional.
      *
+     * PDO metadata handler:
+     * This metadata handler looks up metadata of an entity stored in a database.
+     *
+     * Note: If you are using the PDO metadata handler, you must configure the database
+     * options in this configuration file.
+     *
+     * The PDO metadata handler defines the following options:
+     * - 'type': This is always 'pdo'.
+     *
      *
      * Examples:
      *
@@ -593,6 +657,11 @@ $config = array(
      *     array('type' => 'mdx', server => 'http://mdx.server.com:8080', 'cachedir' => '/var/simplesamlphp/mdx-cache', 'cachelength' => 86400)
      *     ),
      *
+     * This example defines an pdo source.
+     * 'metadata.sources' => array(
+     *     array('type' => 'pdo')
+     *     ),
+     *
      * Default:
      * 'metadata.sources' => array(
      *     array('type' => 'flatfile')
diff --git a/docs/simplesamlphp-database.txt b/docs/simplesamlphp-database.txt
new file mode 100644
index 0000000000000000000000000000000000000000..663fdce150e357cb51cced7c87a38ee0b933b600
--- /dev/null
+++ b/docs/simplesamlphp-database.txt
@@ -0,0 +1,91 @@
+SimpleSAML\Database
+=============================
+
+<!-- 
+	This file is written in Markdown syntax. 
+	For more information about how to use the Markdown syntax, read here:
+	http://daringfireball.net/projects/markdown/syntax
+-->
+
+
+<!-- {{TOC}} -->
+
+Purpose
+-------
+This document covers the SimpleSAML\Database class and is only relevant to anyone writing code for SimpleSAMLphp, including modules, that require a database connection.
+
+The Database class provides a single class that can be used to connect to a database which can be shared by anything within SimpleSAMLphp.
+
+Getting Started
+---------------
+If you are just using the already configured database, which would normally be the case, all you need to do is get the global instance of the Database class.
+
+	$db = SimpleSAML\Database::getInstance();
+
+If there is a requirement to connect to an alternate database server (ex. authenticating users that exist on a different SQL server or database) you can specify an alternate configuration.
+
+	$config = new SimpleSAML_Configuration($myconfigarray, "mymodule/lib/Auth/Source/myauth.php");
+	$db = SimpleSAML\Database::getInstance($config);
+
+That will create a new instance of the database, separate from the global instance, specific to the configuration defined in $myconfigarray. If you are going to specify an alternate config, your configuration array must contain the same keys that exist in the master config (database.dsn, database.username, database.password, database.prefix, etc).
+
+Database Prefix
+---------------
+Administrators can add a prefix to all the table names that this database classes accesses and you should take that in account when querying. Assuming that a prefix has been configured as "sp_":
+
+	$table = $db->applyPrefix("saml20_idp_hosted");
+
+$table would be set to "sp_saml20_idp_hosted"
+
+Querying The Database
+---------------------
+You can query the database through two public functions read() and write() which are fairly self-explanitory when it comes to determining which one to use when querying.
+
+### Writing to The Database
+Since the database class allows administrators to configure master and slave database servers, the write function will always use the master database connection.
+
+The write function takes 2 parameters: SQL, params.
+
+	$table = $db->applyPrefix("test");
+	$values = array(
+		'id' => 20,
+		'data' => 'Some data',
+	);
+	
+	$query = $db->write("INSERT INTO $table (id, data) VALUES (:id, :data)", $values);
+
+The values specified in the $values array will be bound to the placeholders and will be executed on the master. By default, values are binded as PDO::PARAM_STR. If you need to override this, you can specify it in the values array.
+
+	$table = $db->applyPrefix("test");
+	$values = array(
+		'id' => array(20, PDO::PARAM_INT),
+		'data' => 'Some data',
+	);
+	
+	$query = $db->write("INSERT INTO $table (id, data) VALUES (:id, :data)", $values);
+
+You can also skip usage of prepared statements. You should **only** use this if you have a statement that has no user input (ex. CREATE TABLE). If the params variable is explicity set to false, it will skip usage of prepared statements. This is only available when writing to the database.
+
+	$table = $db->applyPrefix("test");
+	$query = $db->write("CREATE TABLE IF NOT EXISTS $table (id INT(16) NOT NULL, data TEXT NOT NULL)", false);
+
+### Reading The Database
+Since the database class allows administrators to configure master and slave database servers, the read function will randomly select a slave server to query. If no slaves are configured, it will read from the master.
+
+The read function takes 2 parameters: SQL, params.
+
+	$table = $db->applyPrefix("test");
+	$values = array(
+		'id' => 20,
+	);
+	
+	$query = $db->read("SELECT * FROM $table WHERE id = :id", $values);
+
+The values specified in the $values array will be bound to the placeholders and will be executed on the selected slave. By default, values are binded as PDO::PARAM_STR. If you need to override this, you can specify it in the values array.
+
+	$table = $db->applyPrefix("test");
+	$values = array(
+		'id' => array(20, PDO::PARAM_INT),
+	);
+	
+	$query = $db->read("SELECT * FROM $table WHERE id = :id", $values);
diff --git a/docs/simplesamlphp-metadata-pdostoragehandler.txt b/docs/simplesamlphp-metadata-pdostoragehandler.txt
new file mode 100644
index 0000000000000000000000000000000000000000..82656fb09be835821cfcf385c543c3f7f1d902e9
--- /dev/null
+++ b/docs/simplesamlphp-metadata-pdostoragehandler.txt
@@ -0,0 +1,79 @@
+PDO Metadata Storage Handler
+=============================
+
+<!-- 
+	This file is written in Markdown syntax. 
+	For more information about how to use the Markdown syntax, read here:
+	http://daringfireball.net/projects/markdown/syntax
+-->
+
+
+<!-- {{TOC}} -->
+
+Introduction
+------------
+
+If you want to run a clustered SimpleSAMLphp IdP service and you would like to have centralized storage for metadata, you can use the PDO metadata storage handler.
+
+The present document explains how to configure SimpleSAMLphp and your database.
+
+
+
+Preparations
+------------
+
+You will need to have the appropriate PDO drivers for your database and you will have to configure the database section within the config/config.php file.
+
+
+
+Configuring SimpleSAMLphp
+-----------------------------
+
+You will first need to configure a PDO metadata source.
+
+	[root@simplesamlphp simplesamlphp]# vi config/config.php
+
+Here is an example of flatfile plus PDO:
+
+	'metadata.sources' => array(
+		array('type' => 'flatfile'),
+		array('type' => 'pdo'),
+	),
+
+
+
+Initializing the Database
+-------------------------
+
+
+Once you have configured your metadata sources to include a PDO source, you will need to initialize the database. This process will create tables in the database for each type of metadata set (saml20-idp-hosted, saml20-idp-remote, saml20-sp-remote, etc).
+
+	[root@simplesamlphp simplesamlphp]# php bin/initMDSPdo.php
+
+If you connect to your database, you will see 11 new empty tables; one for each metadata set.
+
+
+Adding Metadata
+---------------
+
+With the PDO metadata storage handler, metadata is stored in the table for the appropriate set and is stored in JSON format.
+
+As an example, here is the saml20_idp_hosted table:
+
+entity_id       | entity_data
+----------------|-------------------------------------------------------------------------------------------------------------------------
+`__DEFAULT:1__` | {"host":"\_\_DEFAULT\_\_","privatekey":"idp.key","certificate":"idp.crt","auth":"example-ldap","userid.attribute":"uid"}
+
+Another example is the saml20_idp_remote table:
+
+entity_id                | entity_data
+-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+https://openidp.feide.no | {"name":{"en":"Feide OpenIdP - guest users","no":"Feide Gjestebrukere"},"description":"Here you can login with your account on Feide RnD OpenID. If you do not already have an account on this identity provider, you can create a new one by following the create new account link and follow the instructions.","SingleSignOnService":"https:\/\/openidp.feide.no\/simplesaml\/saml2\/idp\/SSOService.php","SingleLogoutService":"https:\/\/openidp.feide.no\/simplesaml\/saml2\/idp\/SingleLogoutService.php","certFingerprint":"c9ed4dfb07caf13fc21e0fec1572047eb8a7a4cb"}
+
+There is an included script in the `bin` directory that will import all flatfile metadata files and store them in the database, but you can use an external tool to maintain the metadata in the database. This document will only cover adding metadata using the included utility, but the tables above should provide enough information if you would like to create a utility to manage your metadata externally.
+
+To import all flatfile metadata files into the PDO database, run the following script
+
+	[root@simplesamlphp simplesamlphp]# php bin/importPdoMetadata.php
+
+In the event that you import a metadata for an entity id that already exists in the database, it will be overwritten.
diff --git a/lib/SimpleSAML/Database.php b/lib/SimpleSAML/Database.php
new file mode 100644
index 0000000000000000000000000000000000000000..85ef915c970768262d670a789a059886e975c1e3
--- /dev/null
+++ b/lib/SimpleSAML/Database.php
@@ -0,0 +1,259 @@
+<?php
+namespace SimpleSAML;
+
+/**
+ * This file implements functions to read and write to a group of database
+ * servers.
+ *
+ * This database class supports a single database, or a master/slave
+ * configuration with as many defined slaves as a user would like.
+ * 
+ * The goal of this class is to provide a single mechanism to connect to a database
+ * that can be reused by any component within SimpleSAMLphp including modules.
+ * When using this class, the global configuration should be passed here, but
+ * in the case of a module that has a good reason to use a different database,
+ * such as sqlauth, an alternative config file can be provided.
+ *
+ * @author Tyler Antonio, University of Alberta. <tantonio@ualberta.ca>
+ * @package SimpleSAMLphp
+ */
+
+class Database
+{
+
+	/**
+	 * This variable holds the instance of the session - Singleton approach.
+	 */
+	private static $instance = array();
+
+	/**
+	 * PDO Object for the Master database server
+	 */
+	private $dbMaster;
+
+	/**
+	 * Array of PDO Objects for configured database
+	 * slaves
+	 */
+	private $dbSlaves = array();
+
+	/**
+	 * Prefix to apply to the tables
+	 */
+	private $tablePrefix;
+
+	/**
+	 * Retrieves the current database instance. Will create a new one if there isn't an existing connection.
+	 *
+	 * @param object $altConfig Optional: Instance of a SimpleSAML_Configuration class
+	 * @return SimpleSAML_Database The shared database connection.
+	 */
+	public static function getInstance($altConfig = null)
+	{
+		$config = ($altConfig)? $altConfig : \SimpleSAML_Configuration::getInstance();
+		$instanceId = self::generateInstanceId($config);
+
+		/* Check if we already have initialized the session. */
+		if (isset(self::$instance[$instanceId])) {
+			return self::$instance[$instanceId];
+		}
+
+		/* Create a new session. */
+		self::$instance[$instanceId] = new Database($config);
+		return self::$instance[$instanceId];
+	}
+
+	/**
+	 * Private constructor that restricts instantiation to getInstance().
+	 *
+	 * @param object $config Instance of the SimpleSAML_Configuration class
+	 */
+	private function __construct($config)
+	{
+		$driverOptions = array();
+		if ($config->getBoolean('database.persistent', TRUE)) {
+			$driverOptions = array(\PDO::ATTR_PERSISTENT => TRUE);
+		}
+
+		// Connect to the master
+		$this->dbMaster = $this->connect($config->getValue('database.dsn'), $config->getValue('database.username'), $config->getValue('database.password'), $driverOptions);
+
+		// Connect to any configured slaves
+		$slaves = $config->getValue('database.slaves');
+		if (count($slaves >= 1)) {
+			foreach ($slaves as $slave) {
+				array_push($this->dbSlaves, $this->connect($slave['dsn'], $slave['username'], $slave['password'], $driverOptions));
+			}
+		}
+
+		$this->tablePrefix = $config->getString('database.prefix', '');
+	}
+
+	/**
+	 * Generate an Instance ID based on the database
+	 * configuration.
+	 *
+	 * @param $config 			Configuration class
+	 *
+	 * @return string $instanceId
+	 */
+	private static function generateInstanceId($config)
+	{
+		$assembledConfig = array(
+			'master' => array(
+				'database.dsn' => $config->getValue('database.dsn'),
+				'database.username' => $config->getValue('database.username'),
+				'database.password' => $config->getValue('database.password'),
+				'database.prefix' => $config->getValue('database.prefix'),
+				'database.persistent' => $config->getValue('database.persistent'),
+			),
+			'slaves' => $config->getValue('database.slaves'),
+		);
+
+		return sha1(serialize($assembledConfig));
+	}
+
+	/**
+	 * This function connects to a dabase.
+	 *
+	 * @param $dsn 			Database connection string
+	 * @param $username 	SQL user
+	 * @param $password 	SQL password
+	 * @param $options 		PDO options
+	 *
+	 * @return PDO object
+	 */
+	private function connect($dsn, $username, $password, $options)
+	{
+		try {
+			$db = new \PDO($dsn, $username, $password, $options);
+			$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+
+			return $db;
+		} catch(\PDOException $e) {
+			throw new \Exception("Database error: ". $e->getMessage());
+		}
+	}
+
+	/**
+	 * This function randomly selects a slave database server
+	 * to query. In the event no slaves are configured, it
+	 * will return the master.
+	 *
+	 * @return PDO object
+	 */
+	private function getSlave()
+	{
+		if (count($this->dbSlaves) > 0) {
+			$slaveId = rand(0,count($this->dbSlaves)-1);
+			return $this->dbSlaves[$slaveId];
+		} else {
+			return $this->dbMaster;
+		}
+	}
+
+	/**
+	 * This function simply applies the table prefix to
+	 * a suppled table name.
+	 *
+	 * @param $table Table to apply prefix,if configured
+	 * @return string Table with configured prefix
+	 */
+	public function applyPrefix($table)
+	{
+		return $this->tablePrefix . $table;
+	}
+
+	/**
+	 * This function queries the database
+	 *
+	 * @param $db 			PDO object to use
+	 * @param $stmt 		Prepared SQL statement
+	 * @param $params 		Parameters
+	 *
+	 * @return PDO statement object
+	 */
+	private function query($db, $stmt, $params)
+	{
+		assert('is_object($db)');
+		assert('is_string($stmt)');
+		assert('is_array($params)');
+
+		try {
+			$query = $db->prepare($stmt);
+
+			foreach ($params as $param => $value) {
+				if (is_array($value)) {
+					$query->bindValue(":$param", $value[0], ($value[1])? $value[1] : \PDO::PARAM_STR);
+				} else {
+					$query->bindValue(":$param", $value, \PDO::PARAM_STR);
+				}
+			}
+
+			$query->execute();
+
+			return $query;
+		} catch (\PDOException $e) {
+			throw new \Exception("Database error: ". $e->getMessage());
+		}
+	}
+
+	/**
+	 * This function queries the database without using a
+	 * prepared statement.
+	 *
+	 * @param $db 			PDO object to use
+	 * @param $stmt 		Prepared SQL statement
+	 * @param $params 		Parameters
+	 *
+	 * @return PDO statement object
+	 */
+	private function exec($db, $stmt)
+	{
+		assert('is_object($db)');
+		assert('is_string($stmt)');
+
+		try {
+			$query = $db->exec($stmt);
+
+			return $query;
+		} catch (\PDOException $e) {
+			throw new \Exception("Database error: ". $e->getMessage());
+		}
+	}
+
+	/**
+	 * This executes queries directly on the master.
+	 *
+	 * @param $stmt 		Prepared SQL statement
+	 * @param $params 		Parameters
+	 *
+	 * @return PDO statement object
+	 */
+	public function write($stmt, $params = array())
+	{
+		$db = $this->dbMaster;
+
+		if (is_array($params)) {
+			return $this->query($db, $stmt, $params);
+		} else {
+			return $this->exec($db, $stmt);
+		}
+	}
+
+	/**
+	 * This executes queries on a database server
+	 * that is determined by this::getSlave()
+	 *
+	 * @param $stmt 		Prepared SQL statement
+	 * @param $params 		Parameters
+	 *
+	 * @return PDO statement object
+	 */
+	public function read($stmt, $params = array())
+	{
+		$db = $this->getSlave();
+
+		return $this->query($db, $stmt, $params);
+	}
+}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
new file mode 100644
index 0000000000000000000000000000000000000000..e493c5b773ffaefb07dc6bde0126568a09bb0614
--- /dev/null
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
@@ -0,0 +1,225 @@
+<?php
+
+/**
+ * Class for handling metadata files stored in a database.
+ *
+ * This class has been based off a previous version written by
+ * mooknarf@gmail.com and patched to work with the latest version
+ * of simpleSAMLphp
+ *
+ * @author Tyler Antonio, University of Alberta <tantonio@ualberta.ca>
+ * @package simpleSAMLphp
+ */
+
+class SimpleSAML_Metadata_MetaDataStorageHandlerPdo extends SimpleSAML_Metadata_MetaDataStorageSource{
+
+	/**
+	 * PDO Database connection string
+	 */
+	private $dsn;
+
+	/**
+	 * The PDO object
+	 */
+	private $db;
+
+	/**
+	 * Prefix to apply to the metadata table
+	 */
+	private $tablePrefix;
+
+	/**
+	 * This is an associative array which stores the different metadata sets we have loaded.
+	 */
+	private $cachedMetadata = array();
+
+	/**
+	 * All the metadata sets supported by this MetaDataStorageHandler
+	 */
+	public $supportedSets = array(
+		'adfs-idp-hosted',
+		'adfs-sp-remote',
+		'saml20-idp-hosted',
+		'saml20-idp-remote',
+		'saml20-sp-remote',
+		'shib13-idp-hosted',
+		'shib13-idp-remote',
+		'shib13-sp-hosted',
+		'shib13-sp-remote',
+		'wsfed-idp-remote',
+		'wsfed-sp-hosted'
+	);
+
+
+	/**
+	 * This constructor initializes the PDO metadata storage handler with the specified
+	 * configuration. The configuration is an associative array with the following
+	 * possible elements (set in config.php):
+	 * - 'usePersistentConnection': TRUE/FALSE if database connection should be
+	 *                				persistent.
+	 *
+	 * - 'dsn': 					The database connection string.
+	 *
+	 * - 'username': 				Database user name
+	 *
+	 * - 'password': 				Password for the database user.
+	 *
+	 * @param array $config  An associtive array with the configuration for this handler.
+	 */
+	public function __construct($config) {
+		assert('is_array($config)');
+
+		$globalConfig = SimpleSAML_Configuration::getInstance();
+		$this->db = SimpleSAML\Database::getInstance();
+
+		$cfgHelp = SimpleSAML_Configuration::loadFromArray($config, 'pdo metadata source');
+	}
+
+
+	/**
+	 * This function loads the given set of metadata from a file to a configured database.
+	 * This function returns NULL if it is unable to locate the given set in the metadata directory.
+	 *
+	 * @param string $set  The set of metadata we are loading.
+	 * @return array $metadata Associative array with the metadata, or NULL if we are unable to load metadata from the given file.
+	 */
+	private function load($set) {
+		assert('is_string($set)');
+
+		$tableName = $this->getTableName($set);
+
+		if (!in_array($set, $this->supportedSets)) {
+			return NULL;
+		}
+
+		$stmt = $this->db->read("SELECT entity_id, entity_data FROM $tableName");
+		if ($stmt->execute()) {
+			$metadata = array();
+
+			while ($d = $stmt->fetch()) {
+				$metadata[$d['entity_id']] = json_decode($d['entity_data'], TRUE);
+			}
+
+			return $metadata;
+		} else {
+			throw new Exception('PDO metadata handler: Database error: ' . var_export($this->pdo->errorInfo(), TRUE));
+		}
+	}
+
+
+	/**
+	 * Retrieve a list of all available metadata for a given set.
+	 *
+	 * @param string $set  The set we are looking for metadata in.
+	 * @return array $metadata An associative array with all the metadata for the given set.
+	 */
+	public function getMetadataSet($set) {
+		assert('is_string($set)');
+
+		if (array_key_exists($set, $this->cachedMetadata)) {
+			return $this->cachedMetadata[$set];
+		}
+
+		$metadataSet = $this->load($set);
+		if ($metadataSet === NULL) {
+			$metadataSet = array();
+		}
+
+		foreach ($metadataSet AS $entityId => &$entry) {
+			if (preg_match('/__DYNAMIC(:[0-9]+)?__/', $entityId)) {
+				$entry['entityid'] = $this->generateDynamicHostedEntityID($set);
+			} else {
+				$entry['entityid'] = $entityId;
+			}
+		}
+
+		$this->cachedMetadata[$set] = $metadataSet;
+		return $metadataSet;
+	}
+
+	private function generateDynamicHostedEntityID($set) {
+		assert('is_string($set)');
+
+		/* Get the configuration. */
+		$baseurl = SimpleSAML_Utilities::getBaseURL();
+
+		if ($set === 'saml20-idp-hosted') {
+			return $baseurl . 'saml2/idp/metadata.php';
+		} elseif($set === 'saml20-sp-hosted') {
+			return $baseurl . 'saml2/sp/metadata.php';			
+		} elseif($set === 'shib13-idp-hosted') {
+			return $baseurl . 'shib13/idp/metadata.php';
+		} elseif($set === 'shib13-sp-hosted') {
+			return $baseurl . 'shib13/sp/metadata.php';
+		} elseif($set === 'wsfed-sp-hosted') {
+			return 'urn:federation:' . SimpleSAML_Utilities::getSelfHost();
+		} elseif($set === 'adfs-idp-hosted') {
+			return 'urn:federation:' . SimpleSAML_Utilities::getSelfHost() . ':idp';
+		} else {
+			throw new Exception('Can not generate dynamic EntityID for metadata of this type: [' . $set . ']');
+		}
+	}
+
+	/**
+	 * Add metadata to the configured database
+	 *
+	 * @param string $index Entity ID
+	 * @param string $set The set to add the metadata to
+	 * @param array $entityData Metadata
+	 * @return bool True/False if entry was sucessfully added
+	 */
+	public function addEntry($index, $set, $entityData) {
+		assert('is_string($index)');
+		assert('is_string($set)');
+		assert('is_array($entityData)');
+
+		if (!in_array($set, $this->supportedSets)) {
+			return FALSE;
+		}
+
+		$tableName = $this->getTableName($set);
+
+		$metadata = $this->db->read("SELECT entity_id, entity_data FROM $tableName WHERE entity_id = :entity_id", array(
+			'entity_id' => $index,
+		));
+
+		$retrivedEntityIDs = $metadata->fetch();
+
+		$params = array(
+			'entity_id' => $index,
+			'entity_data' => json_encode($entityData),
+		);
+
+		if($retrivedEntityIDs !== FALSE && count($retrivedEntityIDs) > 0){
+			$stmt = $this->db->write("UPDATE $tableName SET entity_data = :entity_data WHERE entity_id = :entity_id", $params);
+		} else {
+			$stmt = $this->db->write("INSERT INTO $tableName (entity_id, entity_data) VALUES (:entity_id, :entity_data)", $params);
+		}
+
+		return 1 === $stmt->rowCount();
+	}
+
+	/**
+	 * Replace the -'s to an _ in table names for Metadata sets
+	 * since SQL does not allow a - in a table name.
+	 *
+	 * @param string $table Table
+	 * @return string Replaced table name
+	 */
+	private function getTableName($table) {
+		assert('is_string($table)');
+
+		return $this->db->applyPrefix(str_replace("-", "_", $this->tablePrefix . $table));
+	}
+
+	/**
+	 * Initialize the configured database
+	 */
+	public function initDatabase() {
+		foreach ($this->supportedSets as $set) {
+			$tableName = $this->getTableName($set);
+			$this->db->write("CREATE TABLE IF NOT EXISTS $tableName (entity_id VARCHAR(255) PRIMARY KEY NOT NULL, entity_data TEXT NOT NULL)");
+		}
+	}
+
+}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
index 1fc1160b37a920d5f8f6646484ec36ca93872cb1..55112537289168389e3a6bb354dbe66896308847 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
@@ -68,6 +68,8 @@ abstract class SimpleSAML_Metadata_MetaDataStorageSource {
 				return new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize($sourceConfig);
 			case 'mdx':
 				return new SimpleSAML_Metadata_MetaDataStorageHandlerMDX($sourceConfig);
+			case 'pdo':
+				return new SimpleSAML_Metadata_MetaDataStorageHandlerPdo($sourceConfig);
 			default:
 				throw new Exception('Invalid metadata source type: "' . $type . '".');
 		}
diff --git a/tests/SimpleSAML/DatabaseTest.php b/tests/SimpleSAML/DatabaseTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6a69e3fd8d70d94d5d285f328de0ba3f6ed20f96
--- /dev/null
+++ b/tests/SimpleSAML/DatabaseTest.php
@@ -0,0 +1,246 @@
+<?php
+/**
+ * This test ensures that the SimpleSAML_Database class can properly
+ * query a database.
+ *
+ * It currently uses sqlite to test, but an alternate config.php file
+ * should be created for test cases to ensure that it will work
+ * in an environment.
+ *
+ * @author Tyler Antonio, University of Alberta. <tantonio@ualberta.ca>
+ * @package simpleSAMLphp
+ */
+
+class SimpleSAML_DatabaseTest extends PHPUnit_Framework_TestCase
+{
+	protected $config;
+	protected $db;
+
+	/**
+	 * Make protected functions available for testing
+	 * @requires PHP 5.3.2
+	 */
+	protected static function getMethod($getMethod)
+	{
+		$class = new ReflectionClass('SimpleSAML\Database');
+		$method = $class->getMethod($getMethod);
+		$method->setAccessible(true);
+		return $method;
+	}
+
+	/**
+	 * @covers SimpleSAML\Database::getInstance
+	 * @covers SimpleSAML\Database::generateInstanceId
+	 * @covers SimpleSAML\Database::__construct
+	 * @covers SimpleSAML\Database::connect
+	 */
+	public function setUp()
+	{
+		$config = array(
+			'database.dsn' => 'sqlite::memory:',
+			'database.username' => null,
+			'database.password' => null,
+			'database.prefix' => 'phpunit_',
+			'database.persistent' => true,
+			'database.slaves' => array(),
+		);
+
+		$this->config = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+
+		// Ensure that we have a functional configuration class.
+		$this->assertInstanceOf('SimpleSAML_Configuration', $this->config);
+		$this->assertEquals($config['database.dsn'], $this->config->getValue('database.dsn'));
+
+		$this->db = SimpleSAML\Database::getInstance($this->config);
+
+		// Ensure that we have a functional database class.
+		$this->assertInstanceOf('SimpleSAML\Database', $this->db);
+	}
+
+	/**
+	 * @covers SimpleSAML\Database::getInstance
+	 * @covers SimpleSAML\Database::generateInstanceId
+	 * @covers SimpleSAML\Database::__construct
+	 * @covers SimpleSAML\Database::connect
+	 * @expectedException Exception
+	 * @test
+	 */
+	public function ConnectionFailure()
+	{
+		$config = array(
+			'database.dsn' => 'mysql:host=localhost;dbname=saml',
+			'database.username' => 'notauser',
+			'database.password' => 'notausersinvalidpassword',
+			'database.prefix' => 'phpunit_',
+			'database.persistent' => true,
+			'database.slaves' => array(),
+		);
+
+		$this->config = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+		$db = SimpleSAML\Database::getInstance($this->config);
+	}
+
+	/**
+	 * @covers SimpleSAML\Database::getInstance
+	 * @covers SimpleSAML\Database::generateInstanceId
+	 * @covers SimpleSAML\Database::__construct
+	 * @covers SimpleSAML\Database::connect
+	 * @test
+	 */
+	public function Instances()
+	{
+		$config = array(
+			'database.dsn' => 'sqlite::memory:',
+			'database.username' => null,
+			'database.password' => null,
+			'database.prefix' => 'phpunit_',
+			'database.persistent' => true,
+			'database.slaves' => array(),
+		);
+		$config2 = array(
+			'database.dsn' => 'sqlite::memory:',
+			'database.username' => null,
+			'database.password' => null,
+			'database.prefix' => 'phpunit2_',
+			'database.persistent' => true,
+			'database.slaves' => array(),
+		);
+
+		$config1 = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+		$config2 = new SimpleSAML_Configuration($config2, "test/SimpleSAML/DatabaseTest.php");
+		$config3 = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+
+		$db1 = SimpleSAML\Database::getInstance($config1);
+		$db2 = SimpleSAML\Database::getInstance($config2);
+		$db3 = SimpleSAML\Database::getInstance($config3);
+
+		$generateInstanceId = self::getMethod('generateInstanceId');
+
+		$instance1 = $generateInstanceId->invokeArgs($db1, array($config1));
+		$instance2 = $generateInstanceId->invokeArgs($db2, array($config2));
+		$instance3 = $generateInstanceId->invokeArgs($db3, array($config3));
+
+		// Assert that $instance1 and $instance2 have different instance ids
+		$this->assertNotEquals($instance1, $instance2, "Database instances should be different, but returned the same id");
+		// Assert that $instance1 and $instance3 have identical instance ids
+		$this->assertEquals($instance1, $instance3, "Database instances should have the same id, but returned different id");
+
+		// Assert that $db1 and $db2 are different instances
+		$this->assertNotEquals(spl_object_hash($db1), spl_object_hash($db2), "Database instances should be different, but returned the same spl_object_hash");
+		// Assert that $db1 and $db3 are identical instances
+		$this->assertEquals(spl_object_hash($db1), spl_object_hash($db3), "Database instances should be the same, but returned different spl_object_hash");
+	}
+
+	/**
+	 * @covers SimpleSAML\Database::getInstance
+	 * @covers SimpleSAML\Database::generateInstanceId
+	 * @covers SimpleSAML\Database::__construct
+	 * @covers SimpleSAML\Database::connect
+	 * @covers SimpleSAML\Database::getSlave
+	 * @test
+	 */
+	public function Slaves(){
+		$getSlave = self::getMethod('getSlave');
+		
+		$master = spl_object_hash(PHPUnit_Framework_Assert::readAttribute($this->db, 'dbMaster'));
+		$slave = spl_object_hash($getSlave->invokeArgs($this->db, array()));
+
+		$this->assertTrue(($master == $slave), "getSlave should have returned the master database object");
+
+		$config = array(
+			'database.dsn' => 'sqlite::memory:',
+			'database.username' => null,
+			'database.password' => null,
+			'database.prefix' => 'phpunit_',
+			'database.persistent' => true,
+			'database.slaves' => array(
+				array(
+					'dsn' => 'sqlite::memory:',
+					'username' => null,
+					'password' => null,
+				),
+			),
+		);
+
+		$sspConfiguration = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+		$msdb = SimpleSAML\Database::getInstance($sspConfiguration);
+
+		$slaves = PHPUnit_Framework_Assert::readAttribute($msdb, 'dbSlaves');
+		$gotSlave = spl_object_hash($getSlave->invokeArgs($msdb, array()));
+
+		$this->assertEquals(spl_object_hash($slaves[0]), $gotSlave, "getSlave should have returned a slave database object");
+	}
+
+	/**
+	 * @covers SimpleSAML\Database::applyPrefix
+	 * @test
+	 */
+	public function prefix(){
+		$prefix = $this->config->getValue('database.prefix');
+		$table = "saml20_idp_hosted";
+		$pftable = $this->db->applyPrefix($table);
+
+		$this->assertEquals($prefix . $table, $pftable, "Did not properly apply the table prefix");
+	}
+
+	/**
+	 * @covers SimpleSAML\Database::write
+	 * @covers SimpleSAML\Database::read
+	 * @covers SimpleSAML\Database::exec
+	 * @covers SimpleSAML\Database::query
+	 * @test
+	 */
+	public function Querying()
+	{
+		$table = $this->db->applyPrefix("sspdbt");
+		$this->assertEquals($this->config->getValue('database.prefix') . "sspdbt", $table);
+
+		$this->db->write("CREATE TABLE IF NOT EXISTS $table (ssp_key INT(16) NOT NULL, ssp_value TEXT NOT NULL)", false);
+
+		$query1 = $this->db->read("SELECT * FROM $table");
+		$this->assertEquals(0, $query1->fetch(), "Table $table is not empty when it should be.");
+
+		$ssp_key = time();
+		$ssp_value = md5(rand(0,10000));
+		$stmt = $this->db->write("INSERT INTO $table (ssp_key, ssp_value) VALUES (:ssp_key, :ssp_value)", array('ssp_key' => array($ssp_key, PDO::PARAM_INT), 'ssp_value' => $ssp_value));
+		$this->assertEquals(1, $stmt->rowCount(), "Could not insert data into $table.");
+
+		$query2 = $this->db->read("SELECT * FROM $table WHERE ssp_key = :ssp_key", array('ssp_key' => $ssp_key));
+		$data = $query2->fetch();
+		$this->assertEquals($data['ssp_value'], $ssp_value, "Inserted data doesn't match what is in the database");
+	}
+
+	/**
+	 * @covers SimpleSAML\Database::read
+	 * @covers SimpleSAML\Database::query
+	 * @expectedException Exception
+	 * @test
+	 */
+	public function ReadFailure()
+	{
+		$table = $this->db->applyPrefix("sspdbt");
+		$this->assertEquals($this->config->getValue('database.prefix') . "sspdbt", $table);
+
+		$query = $this->db->read("SELECT * FROM $table");
+	}
+
+	/**
+	 * @covers SimpleSAML\Database::write
+	 * @covers SimpleSAML\Database::exec
+	 * @expectedException Exception
+	 * @test
+	 */
+	public function NoSuchTable()
+	{
+		$this->db->write("DROP TABLE phpunit_nonexistent", false);
+	}
+
+	public function tearDown()
+	{
+		$table = $this->db->applyPrefix("sspdbt");
+		$this->db->write("DROP TABLE IF EXISTS $table", false);
+
+		unset($this->config);
+		unset($this->db);
+	}
+}