Skip to content

Commit 9c716c5

Browse files
Pantheon Automationgreg-1-anderson
Pantheon Automation
authored andcommitted
Update to Drupal 7.89. For more information, see https://www.drupal.org/project/drupal/releases/7.89
1 parent 6befdaa commit 9c716c5

35 files changed

+378
-45
lines changed

CHANGELOG.txt

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
Drupal 7.89, 2022-03-02
2+
-----------------------
3+
- Bug fixes for PHP 8.1
4+
- Fix tests for PostgreSQL
5+
16
Drupal 7.88, 2022-02-15
27
-----------------------
38
- Fixed security issues:

includes/bootstrap.inc

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/**
99
* The current system version.
1010
*/
11-
define('VERSION', '7.88');
11+
define('VERSION', '7.89');
1212

1313
/**
1414
* Core API compatibility.
@@ -1930,7 +1930,7 @@ function format_string($string, array $args = array()) {
19301930
* @ingroup sanitization
19311931
*/
19321932
function check_plain($text) {
1933-
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
1933+
return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8');
19341934
}
19351935

19361936
/**

includes/database/pgsql/database.inc

+2-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
117117
case Database::RETURN_AFFECTED:
118118
return $stmt->rowCount();
119119
case Database::RETURN_INSERT_ID:
120-
return $this->connection->lastInsertId($options['sequence_name']);
120+
$sequence_name = isset($options['sequence_name']) ? $options['sequence_name'] : NULL;
121+
return $this->connection->lastInsertId($sequence_name);
121122
case Database::RETURN_NULL:
122123
return;
123124
default:

includes/database/pgsql/query.inc

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class InsertQuery_pgsql extends InsertQuery {
3030
foreach ($this->insertFields as $idx => $field) {
3131
if (isset($table_information->blob_fields[$field])) {
3232
$blobs[$blob_count] = fopen('php://memory', 'a');
33-
fwrite($blobs[$blob_count], $insert_values[$idx]);
33+
fwrite($blobs[$blob_count], (string) $insert_values[$idx]);
3434
rewind($blobs[$blob_count]);
3535

3636
$stmt->bindParam(':db_insert_placeholder_' . $max_placeholder++, $blobs[$blob_count], PDO::PARAM_LOB);
@@ -182,7 +182,7 @@ class UpdateQuery_pgsql extends UpdateQuery {
182182

183183
if (isset($table_information->blob_fields[$field])) {
184184
$blobs[$blob_count] = fopen('php://memory', 'a');
185-
fwrite($blobs[$blob_count], $value);
185+
fwrite($blobs[$blob_count], (string) $value);
186186
rewind($blobs[$blob_count]);
187187
$stmt->bindParam($placeholder, $blobs[$blob_count], PDO::PARAM_LOB);
188188
++$blob_count;

includes/database/pgsql/schema.inc

+135-13
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,64 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
2323
*/
2424
protected $tableInformation = array();
2525

26+
/**
27+
* The maximum allowed length for index, primary key and constraint names.
28+
*
29+
* Value will usually be set to a 63 chars limit but PostgreSQL allows
30+
* to higher this value before compiling, so we need to check for that.
31+
*
32+
* @var int
33+
*/
34+
protected $maxIdentifierLength;
35+
36+
/**
37+
* Make sure to limit identifiers according to PostgreSQL compiled in length.
38+
*
39+
* PostgreSQL allows in standard configuration identifiers no longer than 63
40+
* chars for table/relation names, indexes, primary keys, and constraints. So
41+
* we map all identifiers that are too long to drupal_base64hash_tag, where
42+
* tag is one of:
43+
* - idx for indexes
44+
* - key for constraints
45+
* - pkey for primary keys
46+
* - seq for sequences
47+
*
48+
* @param string $table_identifier_part
49+
* The first argument used to build the identifier string. This usually
50+
* refers to a table/relation name.
51+
* @param string $column_identifier_part
52+
* The second argument used to build the identifier string. This usually
53+
* refers to one or more column names.
54+
* @param string $tag
55+
* The identifier tag. It can be one of 'idx', 'key', 'pkey' or 'seq'.
56+
*
57+
* @return string
58+
* The index/constraint/pkey identifier.
59+
*/
60+
protected function ensureIdentifiersLength($table_identifier_part, $column_identifier_part, $tag) {
61+
$info = $this->getPrefixInfo($table_identifier_part);
62+
$table_identifier_part = $info['table'];
63+
64+
// Filters out potentially empty $column_identifier_part to ensure
65+
// compatibility with old naming convention (see prefixNonTable()).
66+
$identifiers = array_filter(array($table_identifier_part, $column_identifier_part, $tag));
67+
$identifierName = implode('_', $identifiers);
68+
69+
// Retrieve the max identifier length which is usually 63 characters
70+
// but can be altered before PostgreSQL is compiled so we need to check.
71+
if (empty($this->maxIdentifierLength)) {
72+
$this->maxIdentifierLength = $this->connection->query("SHOW max_identifier_length")->fetchField();
73+
}
74+
75+
if (strlen($identifierName) > $this->maxIdentifierLength) {
76+
$saveIdentifier = 'drupal_' . $this->hashBase64($identifierName) . '_' . $tag;
77+
}
78+
else {
79+
$saveIdentifier = $identifierName;
80+
}
81+
return $saveIdentifier;
82+
}
83+
2684
/**
2785
* Fetch the list of blobs and sequences used on a table.
2886
*
@@ -124,11 +182,11 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
124182

125183
$sql_keys = array();
126184
if (isset($table['primary key']) && is_array($table['primary key'])) {
127-
$sql_keys[] = 'PRIMARY KEY (' . implode(', ', $table['primary key']) . ')';
185+
$sql_keys[] = 'CONSTRAINT ' . $this->ensureIdentifiersLength($name, '', 'pkey') . ' PRIMARY KEY (' . implode(', ', $table['primary key']) . ')';
128186
}
129187
if (isset($table['unique keys']) && is_array($table['unique keys'])) {
130188
foreach ($table['unique keys'] as $key_name => $key) {
131-
$sql_keys[] = 'CONSTRAINT ' . $this->prefixNonTable($name, $key_name, 'key') . ' UNIQUE (' . implode(', ', $key) . ')';
189+
$sql_keys[] = 'CONSTRAINT ' . $this->ensureIdentifiersLength($name, $key_name, 'key') . ' UNIQUE (' . implode(', ', $key) . ')';
132190
}
133191
}
134192

@@ -328,10 +386,31 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
328386
// rename them when renaming the table.
329387
$indexes = $this->connection->query('SELECT indexname FROM pg_indexes WHERE schemaname = :schema AND tablename = :table', array(':schema' => $old_schema, ':table' => $old_table_name));
330388
foreach ($indexes as $index) {
331-
if (preg_match('/^' . preg_quote($old_full_name) . '_(.*)$/', $index->indexname, $matches)) {
389+
// Get the index type by suffix, e.g. idx/key/pkey
390+
$index_type = substr($index->indexname, strrpos($index->indexname, '_') + 1);
391+
392+
// If the index is already rewritten by ensureIdentifiersLength() to not
393+
// exceed the 63 chars limit of PostgreSQL, we need to take care of that.
394+
// Example (drupal_Gk7Su_T1jcBHVuvSPeP22_I3Ni4GrVEgTYlIYnBJkro_idx).
395+
if (strpos($index->indexname, 'drupal_') !== FALSE) {
396+
preg_match('/^drupal_(.*)_' . preg_quote($index_type) . '/', $index->indexname, $matches);
332397
$index_name = $matches[1];
333-
$this->connection->query('ALTER INDEX ' . $index->indexname . ' RENAME TO {' . $new_name . '}_' . $index_name);
334398
}
399+
else {
400+
if ($index_type == 'pkey') {
401+
// Primary keys do not have a specific name in D7.
402+
$index_name = '';
403+
}
404+
else {
405+
// Make sure to remove the suffix from index names, because
406+
// ensureIdentifiersLength() will add the suffix again and thus
407+
// would result in a wrong index name.
408+
preg_match('/^' . preg_quote($old_full_name) . '_(.*)_' . preg_quote($index_type) . '/', $index->indexname, $matches);
409+
$index_name = $matches[1];
410+
}
411+
}
412+
413+
$this->connection->query('ALTER INDEX ' . $index->indexname . ' RENAME TO ' . $this->ensureIdentifiersLength($new_name, $index_name, $index_type));
335414
}
336415

337416
// Now rename the table.
@@ -415,8 +494,8 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
415494
}
416495

417496
public function indexExists($table, $name) {
418-
// Details http://www.postgresql.org/docs/8.3/interactive/view-pg-indexes.html
419-
$index_name = '{' . $table . '}_' . $name . '_idx';
497+
// Details https://www.postgresql.org/docs/10/view-pg-indexes.html
498+
$index_name = $this->ensureIdentifiersLength($table, $name, 'idx');
420499
return (bool) $this->connection->query("SELECT 1 FROM pg_indexes WHERE indexname = '$index_name'")->fetchField();
421500
}
422501

@@ -429,7 +508,18 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
429508
* The name of the constraint (typically 'pkey' or '[constraint]_key').
430509
*/
431510
protected function constraintExists($table, $name) {
432-
$constraint_name = '{' . $table . '}_' . $name;
511+
// ensureIdentifiersLength() expects three parameters, thus we split our
512+
// constraint name in a proper name and a suffix.
513+
if ($name == 'pkey') {
514+
$suffix = $name;
515+
$name = '';
516+
}
517+
else {
518+
$pos = strrpos($name, '_');
519+
$suffix = substr($name, $pos + 1);
520+
$name = substr($name, 0, $pos);
521+
}
522+
$constraint_name = $this->ensureIdentifiersLength($table, $name, $suffix);
433523
return (bool) $this->connection->query("SELECT 1 FROM pg_constraint WHERE conname = '$constraint_name'")->fetchField();
434524
}
435525

@@ -441,15 +531,15 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
441531
throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table @table: primary key already exists.", array('@table' => $table)));
442532
}
443533

444-
$this->connection->query('ALTER TABLE {' . $table . '} ADD PRIMARY KEY (' . implode(',', $fields) . ')');
534+
$this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT ' . $this->ensureIdentifiersLength($table, '', 'pkey') . ' PRIMARY KEY (' . implode(',', $fields) . ')');
445535
}
446536

447537
public function dropPrimaryKey($table) {
448538
if (!$this->constraintExists($table, 'pkey')) {
449539
return FALSE;
450540
}
451541

452-
$this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT ' . $this->prefixNonTable($table, 'pkey'));
542+
$this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT ' . $this->ensureIdentifiersLength($table, '', 'pkey'));
453543
return TRUE;
454544
}
455545

@@ -461,15 +551,15 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
461551
throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key @name to table @table: unique key already exists.", array('@table' => $table, '@name' => $name)));
462552
}
463553

464-
$this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT "' . $this->prefixNonTable($table, $name, 'key') . '" UNIQUE (' . implode(',', $fields) . ')');
554+
$this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT "' . $this->ensureIdentifiersLength($table, $name, 'key') . '" UNIQUE (' . implode(',', $fields) . ')');
465555
}
466556

467557
public function dropUniqueKey($table, $name) {
468558
if (!$this->constraintExists($table, $name . '_key')) {
469559
return FALSE;
470560
}
471561

472-
$this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $this->prefixNonTable($table, $name, 'key') . '"');
562+
$this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $this->ensureIdentifiersLength($table, $name, 'key') . '"');
473563
return TRUE;
474564
}
475565

@@ -489,7 +579,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
489579
return FALSE;
490580
}
491581

492-
$this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name, 'idx'));
582+
$this->connection->query('DROP INDEX ' . $this->ensureIdentifiersLength($table, $name, 'idx'));
493583
return TRUE;
494584
}
495585

@@ -580,7 +670,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
580670
}
581671

582672
protected function _createIndexSql($table, $name, $fields) {
583-
$query = 'CREATE INDEX "' . $this->prefixNonTable($table, $name, 'idx') . '" ON {' . $table . '} (';
673+
$query = 'CREATE INDEX "' . $this->ensureIdentifiersLength($table, $name, 'idx') . '" ON {' . $table . '} (';
584674
$query .= $this->_createKeySql($fields) . ')';
585675
return $query;
586676
}
@@ -614,4 +704,36 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
614704
return $this->connection->query('SELECT obj_description(oid, ?) FROM pg_class WHERE relname = ?', array('pg_class', $info['table']))->fetchField();
615705
}
616706
}
707+
708+
/**
709+
* Calculates a base-64 encoded, PostgreSQL-safe sha-256 hash per PostgreSQL
710+
* documentation: 4.1. Lexical Structure.
711+
*
712+
* @param $data
713+
* String to be hashed.
714+
*
715+
* @return string
716+
* A base-64 encoded sha-256 hash, with + and / replaced with _ and any =
717+
* padding characters removed.
718+
*/
719+
protected function hashBase64($data) {
720+
// Ensure lowercase as D7's pgsql driver does not quote identifiers
721+
// consistently, and they are therefore folded to lowercase by PostgreSQL.
722+
$hash = strtolower(base64_encode(hash('sha256', $data, TRUE)));
723+
// Modify the hash so it's safe to use in PostgreSQL identifiers.
724+
return strtr($hash, array('+' => '_', '/' => '_', '=' => ''));
725+
}
726+
727+
/**
728+
* Build a condition to match a table name against a standard information_schema.
729+
*
730+
* In PostgreSQL "unquoted names are always folded to lower case." The pgsql
731+
* driver does not quote table names, so they are therefore always lowercase.
732+
*
733+
* @see https://www.postgresql.org/docs/14/sql-syntax-lexical.html
734+
*/
735+
protected function buildTableNameCondition($table_name, $operator = '=', $add_prefix = TRUE) {
736+
return parent::buildTableNameCondition(strtolower($table_name), $operator, $add_prefix);
737+
}
738+
617739
}

modules/field/field.crud.inc

+1
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,7 @@ function field_read_instances($params = array(), $include_additional = array())
733733
}
734734

735735
$instances = array();
736+
$query->orderBy('fci.id');
736737
$results = $query->execute();
737738

738739
foreach ($results as $record) {

modules/field/field.info.class.inc

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class FieldInfo {
138138

139139
$map = array();
140140

141-
$query = db_query('SELECT fc.type, fci.field_name, fci.entity_type, fci.bundle FROM {field_config_instance} fci INNER JOIN {field_config} fc ON fc.id = fci.field_id WHERE fc.active = 1 AND fc.storage_active = 1 AND fc.deleted = 0 AND fci.deleted = 0');
141+
$query = db_query('SELECT fc.type, fci.field_name, fci.entity_type, fci.bundle FROM {field_config_instance} fci INNER JOIN {field_config} fc ON fc.id = fci.field_id WHERE fc.active = 1 AND fc.storage_active = 1 AND fc.deleted = 0 AND fci.deleted = 0 ORDER BY bundle, entity_type');
142142
foreach ($query as $row) {
143143
$map[$row->field_name]['bundles'][$row->entity_type][] = $row->bundle;
144144
$map[$row->field_name]['type'] = $row->type;

modules/file/file.field.inc

+1-1
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,7 @@ function theme_file_upload_help($variables) {
943943

944944
$descriptions = array();
945945

946-
if (strlen($description)) {
946+
if (!empty($description)) {
947947
$descriptions[] = $description;
948948
}
949949
if (isset($upload_validators['file_validate_size'])) {

modules/file/tests/file.test

+42-7
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,23 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
749749
foreach (array($field_name2, $field_name) as $each_field_name) {
750750
for ($delta = 0; $delta < 3; $delta++) {
751751
$edit = array('files[' . $each_field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']' => drupal_realpath($test_file->uri));
752+
// drupalPost() takes a $submit parameter that is the value of the
753+
// button whose click we want to emulate. Since we have multiple
754+
// buttons with the value "Upload", and want to control which one we
755+
// use, we change the value of the other ones to something else.
756+
// Since non-clicked buttons aren't included in the submitted POST
757+
// data, and since drupalPost() will result in $this being updated
758+
// with a newly rebuilt form, this doesn't cause problems. Note that
759+
// $buttons is an array of SimpleXMLElement objects passed by
760+
// reference so modifications to each button will affect
761+
// \DrupalWebTestCase::handleForm().
762+
$buttons = $this->xpath('//input[@type="submit" and @value="Upload"]');
763+
$button_name = $each_field_name . '_' . LANGUAGE_NONE . '_' . $delta . '_upload_button';
764+
foreach ($buttons as $button) {
765+
if ($button['name'] != $button_name) {
766+
$button['value'] = 'DUMMY';
767+
}
768+
}
752769
// If the Upload button doesn't exist, drupalPost() will automatically
753770
// fail with an assertion message.
754771
$this->drupalPost(NULL, $edit, t('Upload'));
@@ -786,13 +803,8 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
786803
$button_name = $current_field_name . '_' . LANGUAGE_NONE . '_' . $delta . '_remove_button';
787804
switch ($type) {
788805
case 'nojs':
789-
// drupalPost() takes a $submit parameter that is the value of the
790-
// button whose click we want to emulate. Since we have multiple
791-
// buttons with the value "Remove", and want to control which one we
792-
// use, we change the value of the other ones to something else.
793-
// Since non-clicked buttons aren't included in the submitted POST
794-
// data, and since drupalPost() will result in $this being updated
795-
// with a newly rebuilt form, this doesn't cause problems.
806+
// Same workaround for multiple buttons with the value "Remove" as
807+
// we did for the "Upload" buttons above.
796808
foreach ($buttons as $button) {
797809
if ($button['name'] != $button_name) {
798810
$button['value'] = 'DUMMY';
@@ -1933,3 +1945,26 @@ class FileScanDirectory extends FileFieldTestCase {
19331945
}
19341946

19351947
}
1948+
1949+
/**
1950+
* Test theme implementations declared in file_theme().
1951+
*/
1952+
class FileThemeImplementationsTestCase extends DrupalUnitTestCase {
1953+
1954+
public static function getInfo() {
1955+
return array(
1956+
'name' => 'Theme implementations declared in file_theme()',
1957+
'description' => 'Unit tests theme functions in the file module.',
1958+
'group' => 'File',
1959+
);
1960+
}
1961+
1962+
function testThemeFileUploadHelp() {
1963+
$variables = array(
1964+
'description' => NULL,
1965+
'upload_validators' => NULL,
1966+
);
1967+
$this->assertEqual('', theme_file_upload_help($variables), 'Empty string returned by theme_file_upload_help() with NULL inputs.');
1968+
}
1969+
1970+
}

0 commit comments

Comments
 (0)