2 Commits

Author SHA1 Message Date
chaim d5fe6c21a0 release: v1.2.4 2026-03-29 20:31:11 +00:00
chaim 54e8c77325 fix: proper type coercion for raw SQL import columns
Read column metadata (type, nullable, default) from SHOW COLUMNS and
coerce values accordingly: empty string/false → 0 for NOT NULL numeric
columns, null for nullable ones. Fixes is_internal import error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:31:11 +00:00
2 changed files with 50 additions and 6 deletions
@@ -309,9 +309,12 @@ class EntityImporter
return $newId; return $newId;
} }
/** @var array<string, string[]> Cache of existing columns per table */ /** @var array<string, string[]> Cache of existing column names per table */
private array $tableColumnsCache = []; private array $tableColumnsCache = [];
/** @var array<string, array<string, array>> Cache of column metadata per table */
private array $tableColumnMetaCache = [];
/** @var array<string, string[]> Skipped columns log per entity type */ /** @var array<string, string[]> Skipped columns log per entity type */
private array $skippedColumnsLog = []; private array $skippedColumnsLog = [];
@@ -348,10 +351,8 @@ class EntityImporter
$value = json_encode($value, JSON_UNESCAPED_UNICODE); $value = json_encode($value, JSON_UNESCAPED_UNICODE);
} }
// Fix empty strings for integer/bool columns (MySQL strict mode) // Coerce value to match target column type
if ($value === '' || $value === false) { $value = $this->coerceValue($tableName, $column, $value);
$value = null;
}
$columns[] = "`{$column}`"; $columns[] = "`{$column}`";
$values[] = $value; $values[] = $value;
@@ -377,16 +378,59 @@ class EntityImporter
$pdo = $this->entityManager->getPDO(); $pdo = $this->entityManager->getPDO();
$sth = $pdo->query("SHOW COLUMNS FROM `{$tableName}`"); $sth = $pdo->query("SHOW COLUMNS FROM `{$tableName}`");
$columns = []; $columns = [];
$meta = [];
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) { while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
$columns[] = $row['Field']; $columns[] = $row['Field'];
$meta[$row['Field']] = [
'type' => $row['Type'],
'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'],
];
} }
$this->tableColumnsCache[$tableName] = $columns; $this->tableColumnsCache[$tableName] = $columns;
$this->tableColumnMetaCache[$tableName] = $meta;
return $columns; return $columns;
} }
/**
* Coerce a value to be compatible with the target column type.
*/
private function coerceValue(string $tableName, string $column, mixed $value): mixed
{
$meta = $this->tableColumnMetaCache[$tableName][$column] ?? null;
if ($meta === null) {
return $value;
}
$type = strtolower($meta['type']);
$isNumeric = str_contains($type, 'int') || str_contains($type, 'decimal')
|| str_contains($type, 'float') || str_contains($type, 'double')
|| str_contains($type, 'tinyint') || str_contains($type, 'smallint')
|| str_contains($type, 'bigint');
if ($isNumeric && ($value === '' || $value === false)) {
if ($meta['nullable']) {
return null;
}
return 0;
}
if (!$meta['nullable'] && $value === null) {
if ($isNumeric) {
return 0;
}
return $meta['default'] ?? '';
}
return $value;
}
/** /**
* @return array<string, string[]> entityType => [skipped column names] * @return array<string, string[]> entityType => [skipped column names]
*/ */
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "DataMigration", "name": "DataMigration",
"version": "1.2.3", "version": "1.2.4",
"acceptableVersions": [ "acceptableVersions": [
">=8.0.0" ">=8.0.0"
], ],