From d86c9f376fc8e08e472a78d6ec580b51c6fba6c6 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Mon, 29 Jun 2026 23:10:50 +0200 Subject: [PATCH] perf: optimize _processForeignKeys() Refactors _processForeignKeys() to: - calculate invariant SQL fragments outside the loop - cache escaped table identifier - build SQL string locally before concatenating to the array, saving intermediate string allocations --- system/Database/Forge.php | 55 +++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index f6eb617fd389..a329bf123351 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1072,7 +1072,8 @@ protected function _attributeUnique(array &$attributes, array &$field) */ protected function _attributeAutoIncrement(array &$attributes, array &$field) { - if (! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true + if ( + ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true && str_contains(strtolower($field['type']), 'int') ) { $field['auto_increment'] = ' AUTO_INCREMENT'; @@ -1105,7 +1106,7 @@ protected function _processPrimaryKeys(string $table, bool $asQuery = false): st $sql .= 'CONSTRAINT ' . $this->db->escapeIdentifiers(($this->primaryKeys['keyName'] === '' ? 'pk_' . $table : $this->primaryKeys['keyName'])) - . ' PRIMARY KEY(' . implode(', ', $this->db->escapeIdentifiers($this->primaryKeys['fields'])) . ')'; + . ' PRIMARY KEY(' . implode(', ', $this->db->escapeIdentifiers($this->primaryKeys['fields'])) . ')'; } return $sql; @@ -1225,40 +1226,44 @@ protected function _processForeignKeys(string $table, bool $asQuery = false): ar throw new DatabaseException(lang('Database.fieldNotExists', $errorNames)); } - $sqls = ['']; + $sqls = ['']; + $isOci8 = $this->db->DBDriver === 'OCI8'; + $dbPrefix = $this->db->DBPrefix; + $fkSuffix = $isOci8 ? '_fk' : '_foreign'; - foreach ($this->foreignKeys as $index => $fkey) { - if ($asQuery === false) { - $index = 0; - } else { - $sqls[$index] = ''; - } + // Niezmienne fragmenty SQL przygotowane przed pętlą + $prefixSql = $asQuery + ? 'ALTER TABLE ' . $this->db->escapeIdentifiers($dbPrefix . $table) . ' ADD ' + : ",\n\t"; - $nameIndex = $fkey['fkName'] !== '' ? - $fkey['fkName'] : - $table . '_' . implode('_', $fkey['field']) . ($this->db->DBDriver === 'OCI8' ? '_fk' : '_foreign'); - - $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); - $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); - $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); - $referenceFieldFilled = implode(', ', $this->db->escapeIdentifiers($fkey['referenceField'])); + foreach ($this->foreignKeys as $index => $fkey) { + $idx = $asQuery ? $index : 0; if ($asQuery) { - $sqls[$index] .= 'ALTER TABLE ' . $this->db->escapeIdentifiers($this->db->DBPrefix . $table) . ' ADD '; - } else { - $sqls[$index] .= ",\n\t"; + $sqls[$idx] = ''; } - $formatSql = 'CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s)'; - $sqls[$index] .= sprintf($formatSql, $nameIndexFilled, $foreignKeyFilled, $referenceTableFilled, $referenceFieldFilled); + $nameIndex = $fkey['fkName'] !== '' + ? $fkey['fkName'] + : $table . '_' . implode('_', $fkey['field']) . $fkSuffix; + + $sql = $prefixSql . sprintf( + 'CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s)', + $this->db->escapeIdentifiers($nameIndex), + implode(', ', $this->db->escapeIdentifiers($fkey['field'])), + $this->db->escapeIdentifiers($dbPrefix . $fkey['referenceTable']), + implode(', ', $this->db->escapeIdentifiers($fkey['referenceField'])), + ); if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $this->fkAllowActions, true)) { - $sqls[$index] .= ' ON DELETE ' . $fkey['onDelete']; + $sql .= ' ON DELETE ' . $fkey['onDelete']; } - if ($this->db->DBDriver !== 'OCI8' && $fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $this->fkAllowActions, true)) { - $sqls[$index] .= ' ON UPDATE ' . $fkey['onUpdate']; + if (! $isOci8 && $fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $this->fkAllowActions, true)) { + $sql .= ' ON UPDATE ' . $fkey['onUpdate']; } + + $sqls[$idx] .= $sql; } return $sqls;