diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php index 9b987546d0d1ed64984f3fffc8cc87cc78f99246..4a5f4044a25d7ea8f1173cf2de322724c952c59c 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php @@ -38,6 +38,13 @@ class Schema extends DatabaseSchema { */ protected $maxIdentifierLength; + /** + * PostgreSQL's temporary namespace name. + * + * @var string + */ + protected $tempNamespaceName; + /** * Make sure to limit identifiers according to PostgreSQL compiled in length. * @@ -97,38 +104,36 @@ public function queryTableInformation($table) { $key = 'public.' . $key; } else { - $schema = $this->connection->query('SELECT nspname FROM pg_namespace WHERE oid = pg_my_temp_schema()')->fetchField(); - $key = $schema . '.' . $key; + $key = $this->getTempNamespaceName() . '.' . $key; } if (!isset($this->tableInformation[$key])) { - // Split the key into schema and table for querying. - list($schema, $table_name) = explode('.', $key); $table_information = (object) array( 'blob_fields' => array(), 'sequences' => array(), ); - // Don't use {} around information_schema.columns table. $this->connection->addSavepoint(); try { - // Check if the table information exists in the PostgreSQL metadata. - $table_information_exists = (bool) $this->connection->query("SELECT 1 FROM pg_class WHERE relname = :table", array(':table' => $table_name))->fetchField(); - - // If the table information does not yet exist in the PostgreSQL - // metadata, then return the default table information here, so that it - // will not be cached. - if (!$table_information_exists) { - $this->connection->releaseSavepoint(); - return $table_information; - } - else { - $result = $this->connection->query("SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_schema = :schema AND table_name = :table AND (data_type = 'bytea' OR (numeric_precision IS NOT NULL AND column_default LIKE :default))", array( - ':schema' => $schema, - ':table' => $table_name, - ':default' => '%nextval%', - )); - } + // The bytea columns and sequences for a table can be found in + // pg_attribute, which is significantly faster than querying the + // information_schema. The data type of a field can be found by lookup + // of the attribute ID, and the default value must be extracted from the + // node tree for the attribute definition instead of the historical + // human-readable column, adsrc. + $sql = <<<'EOD' +SELECT pg_attribute.attname AS column_name, format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS data_type, pg_get_expr(pg_attrdef.adbin, pg_attribute.attrelid) AS column_default +FROM pg_attribute +LEFT JOIN pg_attrdef ON pg_attrdef.adrelid = pg_attribute.attrelid AND pg_attrdef.adnum = pg_attribute.attnum +WHERE pg_attribute.attnum > 0 +AND NOT pg_attribute.attisdropped +AND pg_attribute.attrelid = :key::regclass +AND (format_type(pg_attribute.atttypid, pg_attribute.atttypmod) = 'bytea' +OR pg_attrdef.adsrc LIKE 'nextval%') +EOD; + $result = $this->connection->query($sql, [ + ':key' => $key, + ]); } catch (\Exception $e) { $this->connection->rollbackSavepoint(); @@ -136,6 +141,13 @@ public function queryTableInformation($table) { } $this->connection->releaseSavepoint(); + // If the table information does not yet exist in the PostgreSQL + // metadata, then return the default table information here, so that it + // will not be cached. + if (empty($result)) { + return $table_information; + } + foreach ($result as $column) { if ($column->data_type == 'bytea') { $table_information->blob_fields[$column->column_name] = TRUE; @@ -153,6 +165,19 @@ public function queryTableInformation($table) { return $this->tableInformation[$key]; } + /** + * Gets PostgreSQL's temporary namespace name. + * + * @return string + * PostgreSQL's temporary namespace name. + */ + protected function getTempNamespaceName() { + if (!isset($this->tempNamespaceName)) { + $this->tempNamespaceName = $this->connection->query('SELECT nspname FROM pg_namespace WHERE oid = pg_my_temp_schema()')->fetchField(); + } + return $this->tempNamespaceName; + } + /** * Resets information about table blobs, sequences and serial fields. *