From 97086760dc6d37418491a9082ea015e592d560ab Mon Sep 17 00:00:00 2001 From: William Hearn Date: Thu, 17 Nov 2016 22:57:32 -0500 Subject: [PATCH] Issue #998898 by stefan.r, Damien Tournoud, mradcliffe, bzrudi71, Josh Waihi, Stevel, chx, chalet16: Make sure that the identifiers are not more the 63 characters on PostgreSQL --- includes/database/pgsql/schema.inc | 78 +++++++++++++++++++++++++++++++++----- includes/database/schema.inc | 12 ------ 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/includes/database/pgsql/schema.inc b/includes/database/pgsql/schema.inc index f5774de..7b30226 100644 --- a/includes/database/pgsql/schema.inc +++ b/includes/database/pgsql/schema.inc @@ -23,6 +23,50 @@ class DatabaseSchema_pgsql extends DatabaseSchema { */ protected $tableInformation = array(); + + /** + * The maximum allowed length for index, primary key and constraint names. + * + * Value will usually be set to a 63 chars limit but PostgreSQL allows + * to higher this value before compiling, so we need to check for that. + * + * @var integer + */ + protected $maxIdentifierLength; + + /** + * Make sure to limit identifiers according to PostgreSQL compiled in length. + * + * PostgreSQL allows in standard configuration no longer identifiers than 63 chars for + * table/relation names, indexes, primary keys, and constraints. So we map all to long + * identifiers to drupal_base64hash_tag, where tag is one of: + * - idx for indexes + * - key for constraints + * - pkey for primary keys + * + * @param $identifiers + * The arguments to build the identifier string + * @return + * The index/constraint/pkey identifier + */ + protected function ensureIdentifiersLength($identifier) { + $args = func_get_args(); + $info = $this->getPrefixInfo($identifier); + $args[0] = $info['table']; + $identifierName = implode('__', $args); + + // Retrieve the max identifier length which is usually 63 characters + // but can be altered before PostreSQL is compiled so we need to check. + $this->maxIdentifierLength = $this->connection->query("SHOW max_identifier_length")->fetchField(); + + if (strlen($identifierName) > $this->maxIdentifierLength) { + $saveIdentifier = 'drupal_' . $this->hashBase64($identifierName) . '_' . $args[2]; + } else { + $saveIdentifier = $identifierName; + } + return $saveIdentifier; + } + /** * Fetch the list of blobs and sequences used on a table. * @@ -128,7 +172,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { } if (isset($table['unique keys']) && is_array($table['unique keys'])) { foreach ($table['unique keys'] as $key_name => $key) { - $sql_keys[] = 'CONSTRAINT ' . $this->prefixNonTable($name, $key_name, 'key') . ' UNIQUE (' . implode(', ', $key) . ')'; + $sql_keys[] = 'CONSTRAINT ' . $this->ensureIdentifiersLength($name, $key_name, 'key') . ' UNIQUE (' . implode(', ', $key) . ')'; } } @@ -330,7 +374,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { foreach ($indexes as $index) { if (preg_match('/^' . preg_quote($old_full_name) . '_(.*)$/', $index->indexname, $matches)) { $index_name = $matches[1]; - $this->connection->query('ALTER INDEX ' . $index->indexname . ' RENAME TO {' . $new_name . '}_' . $index_name); + $this->connection->query('ALTER INDEX ' . $index->indexname . ' RENAME TO ' . $this->ensureIdentifiersLength($new_name, $index_name, 'idx')); } } @@ -416,7 +460,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { public function indexExists($table, $name) { // Details http://www.postgresql.org/docs/8.3/interactive/view-pg-indexes.html - $index_name = '{' . $table . '}_' . $name . '_idx'; + $index_name = $this->ensureIdentifiersLength($table, $name, 'idx'); return (bool) $this->connection->query("SELECT 1 FROM pg_indexes WHERE indexname = '$index_name'")->fetchField(); } @@ -429,7 +473,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { * The name of the constraint (typically 'pkey' or '[constraint]_key'). */ protected function constraintExists($table, $name) { - $constraint_name = '{' . $table . '}_' . $name; + $constraint_name = $this->ensureIdentifiersLength($table, $name); return (bool) $this->connection->query("SELECT 1 FROM pg_constraint WHERE conname = '$constraint_name'")->fetchField(); } @@ -449,7 +493,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { return FALSE; } - $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT ' . $this->prefixNonTable($table, 'pkey')); + $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT ' . $this->ensureIdentifiersLength($table, 'pkey')); return TRUE; } @@ -461,7 +505,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key @name to table @table: unique key already exists.", array('@table' => $table, '@name' => $name))); } - $this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT "' . $this->prefixNonTable($table, $name, 'key') . '" UNIQUE (' . implode(',', $fields) . ')'); + $this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT "' . $this->ensureIdentifiersLength($table, $name, 'key') . '" UNIQUE (' . implode(',', $fields) . ')'); } public function dropUniqueKey($table, $name) { @@ -469,7 +513,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { return FALSE; } - $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $this->prefixNonTable($table, $name, 'key') . '"'); + $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $this->ensureIdentifiersLength($table, $name, 'key') . '"'); return TRUE; } @@ -489,7 +533,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { return FALSE; } - $this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name, 'idx')); + $this->connection->query('DROP INDEX ' . $this->ensureIdentifiersLength($table, $name, 'idx')); return TRUE; } @@ -580,7 +624,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { } protected function _createIndexSql($table, $name, $fields) { - $query = 'CREATE INDEX "' . $this->prefixNonTable($table, $name, 'idx') . '" ON {' . $table . '} ('; + $query = 'CREATE INDEX "' . $this->ensureIdentifiersLength($table, $name, 'idx') . '" ON {' . $table . '} ('; $query .= $this->_createKeySql($fields) . ')'; return $query; } @@ -614,4 +658,20 @@ class DatabaseSchema_pgsql extends DatabaseSchema { return $this->connection->query('SELECT obj_description(oid, ?) FROM pg_class WHERE relname = ?', array('pg_class', $info['table']))->fetchField(); } } + + /** + * Calculates a base-64 encoded, PostgreSQL-safe sha-256 hash per PostgreSQL + * documentation: 4.1. Lexical Structure. + * + * @param $data + * String to be hashed. + * @return string + * A base-64 encoded sha-256 hash, with + and / replaced with _ and any = + * padding characters removed. + */ + protected function hashBase64($data) { + $hash = base64_encode(hash('sha256', $data, TRUE)); + // Modify the hash so it's safe to use in PostgreSQL identifiers. + return strtr($hash, array('+' => '_', '/' => '_', '=' => '')); + } } diff --git a/includes/database/schema.inc b/includes/database/schema.inc index d8344c6..31d7318 100644 --- a/includes/database/schema.inc +++ b/includes/database/schema.inc @@ -252,18 +252,6 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { } /** - * Create names for indexes, primary keys and constraints. - * - * This prevents using {} around non-table names like indexes and keys. - */ - function prefixNonTable($table) { - $args = func_get_args(); - $info = $this->getPrefixInfo($table); - $args[0] = $info['table']; - return implode('_', $args); - } - - /** * Build a condition to match a table name against a standard information_schema. * * The information_schema is a SQL standard that provides information about the -- 2.5.4 (Apple Git-61)