diff options
author | Nathan Haug <nate@quicksketch.org> | 2011-10-30 16:32:37 -0700 |
---|---|---|
committer | Nathaniel <catch@35733.no-reply.drupal.org> | 2011-11-01 12:48:40 +0900 |
commit | 06fb770bd340e5a18555e0da55a4dd6ba22f76ba (patch) | |
tree | e814bc08d51db3af473ae5be565219c0249b7870 /core/includes/database/pgsql/database.inc | |
parent | 906a6db34707d27120dddd43a23f26f24153255d (diff) | |
download | drupal-06fb770bd340e5a18555e0da55a4dd6ba22f76ba.tar.gz drupal-06fb770bd340e5a18555e0da55a4dd6ba22f76ba.zip |
Issue #22336 by quicksketch, scor, boombatower, and rfay. Move all core Drupal files under a core subdirectory.
Diffstat (limited to 'core/includes/database/pgsql/database.inc')
-rw-r--r-- | core/includes/database/pgsql/database.inc | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/core/includes/database/pgsql/database.inc b/core/includes/database/pgsql/database.inc new file mode 100644 index 00000000000..39b4e9b6960 --- /dev/null +++ b/core/includes/database/pgsql/database.inc @@ -0,0 +1,203 @@ +<?php + +/** + * @file + * Database interface code for PostgreSQL database servers. + */ + +/** + * @ingroup database + * @{ + */ + +/** + * The name by which to obtain a lock for retrive the next insert id. + */ +define('POSTGRESQL_NEXTID_LOCK', 1000); + +class DatabaseConnection_pgsql extends DatabaseConnection { + + public function __construct(array $connection_options = array()) { + // This driver defaults to transaction support, except if explicitly passed FALSE. + $this->transactionSupport = !isset($connection_options['transactions']) || ($connection_options['transactions'] !== FALSE); + + // Transactional DDL is always available in PostgreSQL, + // but we'll only enable it if standard transactions are. + $this->transactionalDDLSupport = $this->transactionSupport; + + // Default to TCP connection on port 5432. + if (empty($connection_options['port'])) { + $connection_options['port'] = 5432; + } + + // PostgreSQL in trust mode doesn't require a password to be supplied. + if (empty($connection_options['password'])) { + $connection_options['password'] = NULL; + } + // If the password contains a backslash it is treated as an escape character + // http://bugs.php.net/bug.php?id=53217 + // so backslashes in the password need to be doubled up. + // The bug was reported against pdo_pgsql 1.0.2, backslashes in passwords + // will break on this doubling up when the bug is fixed, so check the version + //elseif (phpversion('pdo_pgsql') < 'version_this_was_fixed_in') { + else { + $connection_options['password'] = str_replace('\\', '\\\\', $connection_options['password']); + } + + $this->connectionOptions = $connection_options; + + $dsn = 'pgsql:host=' . $connection_options['host'] . ' dbname=' . $connection_options['database'] . ' port=' . $connection_options['port']; + parent::__construct($dsn, $connection_options['username'], $connection_options['password'], array( + // Prepared statements are most effective for performance when queries + // are recycled (used several times). However, if they are not re-used, + // prepared statements become ineffecient. Since most of Drupal's + // prepared queries are not re-used, it should be faster to emulate + // the preparation than to actually ready statements for re-use. If in + // doubt, reset to FALSE and measure performance. + PDO::ATTR_EMULATE_PREPARES => TRUE, + // Convert numeric values to strings when fetching. + PDO::ATTR_STRINGIFY_FETCHES => TRUE, + // Force column names to lower case. + PDO::ATTR_CASE => PDO::CASE_LOWER, + )); + + // Force PostgreSQL to use the UTF-8 character set by default. + $this->exec("SET NAMES 'UTF8'"); + } + + public function query($query, array $args = array(), $options = array()) { + + $options += $this->defaultOptions(); + + // The PDO PostgreSQL driver has a bug which + // doesn't type cast booleans correctly when + // parameters are bound using associative + // arrays. + // See http://bugs.php.net/bug.php?id=48383 + foreach ($args as &$value) { + if (is_bool($value)) { + $value = (int) $value; + } + } + + try { + if ($query instanceof DatabaseStatementInterface) { + $stmt = $query; + $stmt->execute(NULL, $options); + } + else { + $this->expandArguments($query, $args); + $stmt = $this->prepareQuery($query); + $stmt->execute($args, $options); + } + + switch ($options['return']) { + case Database::RETURN_STATEMENT: + return $stmt; + case Database::RETURN_AFFECTED: + return $stmt->rowCount(); + case Database::RETURN_INSERT_ID: + return $this->lastInsertId($options['sequence_name']); + case Database::RETURN_NULL: + return; + default: + throw new PDOException('Invalid return directive: ' . $options['return']); + } + } + catch (PDOException $e) { + if ($options['throw_exception']) { + // Add additional debug information. + if ($query instanceof DatabaseStatementInterface) { + $e->query_string = $stmt->getQueryString(); + } + else { + $e->query_string = $query; + } + $e->args = $args; + throw $e; + } + return NULL; + } + } + + public function queryRange($query, $from, $count, array $args = array(), array $options = array()) { + return $this->query($query . ' LIMIT ' . (int) $count . ' OFFSET ' . (int) $from, $args, $options); + } + + public function queryTemporary($query, array $args = array(), array $options = array()) { + $tablename = $this->generateTemporaryTableName(); + $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE {' . $tablename . '} AS SELECT', $query), $args, $options); + return $tablename; + } + + public function driver() { + return 'pgsql'; + } + + public function databaseType() { + return 'pgsql'; + } + + public function mapConditionOperator($operator) { + static $specials; + + // Function calls not allowed in static declarations, thus this method. + if (!isset($specials)) { + $specials = array( + // In PostgreSQL, 'LIKE' is case-sensitive. For case-insensitive LIKE + // statements, we need to use ILIKE instead. + 'LIKE' => array('operator' => 'ILIKE'), + 'NOT LIKE' => array('operator' => 'NOT ILIKE'), + ); + } + + return isset($specials[$operator]) ? $specials[$operator] : NULL; + } + + /** + * Retrive a the next id in a sequence. + * + * PostgreSQL has built in sequences. We'll use these instead of inserting + * and updating a sequences table. + */ + public function nextId($existing = 0) { + + // Retrive the name of the sequence. This information cannot be cached + // because the prefix may change, for example, like it does in simpletests. + $sequence_name = $this->makeSequenceName('sequences', 'value'); + + // When PostgreSQL gets a value too small then it will lock the table, + // retry the INSERT and if it's still too small then alter the sequence. + $id = $this->query("SELECT nextval('" . $sequence_name . "')")->fetchField(); + if ($id > $existing) { + return $id; + } + + // PostgreSQL advisory locks are simply locks to be used by an + // application such as Drupal. This will prevent other Drupal proccesses + // from altering the sequence while we are. + $this->query("SELECT pg_advisory_lock(" . POSTGRESQL_NEXTID_LOCK . ")"); + + // While waiting to obtain the lock, the sequence may have been altered + // so lets try again to obtain an adequate value. + $id = $this->query("SELECT nextval('" . $sequence_name . "')")->fetchField(); + if ($id > $existing) { + $this->query("SELECT pg_advisory_unlock(" . POSTGRESQL_NEXTID_LOCK . ")"); + return $id; + } + + // Reset the sequence to a higher value than the existing id. + $this->query("ALTER SEQUENCE " . $sequence_name . " RESTART WITH " . ($existing + 1)); + + // Retrive the next id. We know this will be as high as we want it. + $id = $this->query("SELECT nextval('" . $sequence_name . "')")->fetchField(); + + $this->query("SELECT pg_advisory_unlock(" . POSTGRESQL_NEXTID_LOCK . ")"); + + return $id; + } +} + +/** + * @} End of "ingroup database". + */ |