function Update10101::update

Same name and namespace in other branches
  1. 10 core/modules/pgsql/src/Update10101.php \Drupal\pgsql\Update10101::update()

Update *all* existing sequences to include the owner tables.

Parameters

array $sandbox: Stores information for batch updates.

Return value

\Drupal\Core\StringTranslation\PluralTranslatableMarkup|null Returns the amount of orphaned sequences fixed.

File

core/modules/pgsql/src/Update10101.php, line 70

Class

Update10101
An update class for sequence ownership.

Namespace

Drupal\pgsql

Code

public function update(array &$sandbox) : ?PluralTranslatableMarkup {
    if ($this->connection
        ->databaseType() !== 'pgsql') {
        // This database update is a no-op for all other core database drivers.
        $sandbox['#finished'] = 1;
        return NULL;
    }
    if (!isset($sandbox['progress'])) {
        $sandbox['fixed'] = 0;
        $sandbox['progress'] = 0;
        $sandbox['tables'] = [];
        // Discovers all tables defined with hook_schema().
        // @todo We need to add logic to do the same for on-demand tables. See
        //   https://www.drupal.org/i/3358777
        $modules = $this->moduleExtensionList
            ->getList();
        foreach ($modules as $extension) {
            $module = $extension->getName();
            $this->moduleHandler
                ->loadInclude($module, 'install');
            $schema = $this->moduleHandler
                ->invoke($module, 'schema');
            if (!empty($schema)) {
                foreach ($schema as $table_name => $table_info) {
                    foreach ($table_info['fields'] as $column_name => $column_info) {
                        if (str_starts_with($column_info['type'], 'serial')) {
                            $sandbox['tables'][] = [
                                'table' => $table_name,
                                'column' => $column_name,
                            ];
                        }
                    }
                }
            }
        }
        // Discovers all content entity types with integer entity keys that are
        // most likely serial columns.
        $entity_types = $this->entityTypeManager
            ->getDefinitions();
        
        /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
        foreach ($entity_types as $entity_type) {
            $storage_class = $entity_type->getStorageClass();
            if (is_subclass_of($storage_class, SqlContentEntityStorage::class)) {
                $id_key = $entity_type->getKey('id');
                $revision_key = $entity_type->getKey('revision');
                $original_storage_definitions = $this->entityLastInstalledSchemaRepository
                    ->getLastInstalledFieldStorageDefinitions($entity_type->id());
                if ($original_storage_definitions[$id_key]->getType() === 'integer') {
                    $sandbox['tables'][] = [
                        'table' => $entity_type->getBaseTable(),
                        'column' => $id_key,
                    ];
                }
                if ($entity_type->isRevisionable() && $original_storage_definitions[$revision_key]->getType() === 'integer') {
                    $sandbox['tables'][] = [
                        'table' => $entity_type->getRevisionTable(),
                        'column' => $revision_key,
                    ];
                }
            }
        }
        $sandbox['max'] = count($sandbox['tables']);
    }
    else {
        // Adds ownership of orphan sequences to tables.
        $to_process = array_slice($sandbox['tables'], $sandbox['progress'], 50);
        // Ensures that a sequence is not owned first, then ensures that the a
        // sequence exists at all before trying to alter it.
        foreach ($to_process as $table_info) {
            if ($this->connection
                ->schema()
                ->tableExists($table_info['table'])) {
                $owned = (bool) $this->getSequenceName($table_info['table'], $table_info['column']);
                if (!$owned) {
                    $sequence_name = $this->connection
                        ->makeSequenceName($table_info['table'], $table_info['column']);
                    $exists = $this->sequenceExists($sequence_name);
                    if ($exists) {
                        $transaction = $this->connection
                            ->startTransaction($sequence_name);
                        try {
                            $this->updateSequenceOwnership($sequence_name, $table_info['table'], $table_info['column']);
                            $sandbox['fixed']++;
                        } catch (DatabaseExceptionWrapper $e) {
                            $transaction->rollBack();
                        }
                    }
                }
            }
            $sandbox['progress']++;
        }
    }
    if ($sandbox['max'] && $sandbox['progress'] < $sandbox['max']) {
        $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
        return NULL;
    }
    else {
        $sandbox['#finished'] = 1;
        return new PluralTranslatableMarkup($sandbox['fixed'], '1 orphaned sequence fixed.', '@count orphaned sequences fixed');
    }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.