- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
3.6 KiB
3.6 KiB
Database Migration Fix: Column Existence Check
Issue Description
When running migrations, particularly the AddSizeToImageVariantsTable migration, the following error occurred:
Running migrations...
Migrating: AddSizeToImageVariantsTable - Add Size to Image Slot Table
❌ Migration failed: Migration AddSizeToImageVariantsTable failed: Failed to execute query: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '?' at line 1 --- SQL: SHOW COLUMNS FROM image_variants LIKE ? PARAMETERS: {size}
Root Cause
The issue was in the hasColumn method of the Schema class. When checking if a column exists in a MySQL/MariaDB database, the code was using a parameterized query with a placeholder (?) in the LIKE clause:
'mysql' => "SHOW COLUMNS FROM {$table} LIKE ?",
However, MariaDB doesn't support parameter binding for the LIKE clause in this context. The placeholder ? was being passed directly to the SQL statement instead of being replaced with the actual value.
Solution
The solution was to modify the hasColumn method to use a different query for MySQL/MariaDB that properly supports parameter binding:
public function hasColumn(string $table, string $column): bool
{
$driver = $this->getDriverName();
$query = match($driver) {
'mysql' => "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?",
'pgsql' => "SELECT column_name FROM information_schema.columns WHERE table_name = ? AND column_name = ?",
'sqlite' => "PRAGMA table_info({$table})",
default => throw new \RuntimeException("Unsupported driver: {$driver}")
};
if ($driver === 'pgsql') {
$result = $this->connection->queryScalar($query, [$table, $column]);
} elseif ($driver === 'sqlite') {
$columns = $this->connection->query($query)->fetchAll();
foreach ($columns as $col) {
if ($col['name'] === $column) {
return true;
}
}
return false;
} elseif ($driver === 'mysql') {
$result = $this->connection->queryScalar($query, [$table, $column]);
} else {
throw new \RuntimeException("Unsupported driver: {$driver}");
}
return (bool) $result;
}
Key changes:
- Changed the MySQL query to use
information_schema.COLUMNSwith proper parameter binding - Updated the parameter handling for MySQL to pass both table and column parameters
- Added an explicit condition for the MySQL driver to handle the parameters correctly
Benefits
This change:
- Fixes the SQL syntax error when checking if a column exists
- Makes the code more robust by using parameterized queries throughout
- Provides better protection against SQL injection
- Makes the migrations idempotent (can be run multiple times without error)
Future Considerations
For all database operations, especially in schema manipulation:
- Always use parameterized queries when possible
- Test database operations with different database engines
- Consider adding more comprehensive error handling in database schema operations
- Add unit tests for database schema operations to catch these issues earlier