fix(core): una fecha ausente solo se rellena si la columna es NOT NULL sin DEFAULT

This commit is contained in:
Jordan Diaz
2026-09-24 00:17:04 +00:00
parent 14ddf1608e
commit 184038b786
+52 -8
View File
@@ -87,9 +87,13 @@ class AcaiAPI
$lastId = 0; $lastId = 0;
$count = 0; $count = 0;
// Columnas que MySQL exige en un INSERT (NOT NULL sin DEFAULT): solo a
// esas se les pone valor si no vienen. null = no se pudo saber.
$requiredCols = $ignoreSchema ? [] : self::requiredColumns($db, $prefix . $table);
foreach ($records as $record) { foreach ($records as $record) {
list($setParts, $params, $uploadFields) = self::buildSetClause( list($setParts, $params, $uploadFields) = self::buildSetClause(
$record, $schema, $functions, $ignoreSchema, true, $forceNum $record, $schema, $functions, $ignoreSchema, true, $forceNum, $requiredCols
); );
if (empty($setParts)) continue; if (empty($setParts)) continue;
@@ -1366,7 +1370,8 @@ class AcaiAPI
array $functions, array $functions,
$ignoreSchema, $ignoreSchema,
$isInsert, $isInsert,
$forceNum $forceNum,
$requiredCols = null
) { ) {
$now = date('Y-m-d H:i:s'); $now = date('Y-m-d H:i:s');
$user = 1; $user = 1;
@@ -1439,15 +1444,21 @@ class AcaiAPI
$params[] = time(); $params[] = time();
} }
// Campos `date` que NO vienen en el alta -> fecha actual, como el // Campos `date` que NO vienen en el alta -> fecha actual, SOLO si la
// Admin clasico (save.php: "default date fields to current date"). // columna es NOT NULL sin DEFAULT (MySQL rechazaria el INSERT sin
// El bloque de campos de usuario, abajo, solo cubre la fecha que // ella: cms_vacantes.fecha_publicacion). En una columna que admite
// llega VACIA; si la clave falta, una columna `datetime NOT NULL` // NULL, vacio SIGNIFICA algo (notas_internas.deleted_at = no
// sin DEFAULT (cms_vacantes.fecha_publicacion) tumbaba el INSERT. // borrada, cms_users.fecha_caducidad = no caduca): rellenarla con
if (!$ignoreSchema && is_array($schema)) { // "ahora" marcaba cada nota nueva como borrada (regresion de
// 14ddf16). Si no se sabe que columnas exige ($requiredCols null)
// no se rellena nada: mejor un error claro que un dato falso. Un
// `date` del schema sin columna real tampoco entra (no esta en la
// lista).
if (!$ignoreSchema && is_array($schema) && is_array($requiredCols)) {
foreach ($schema as $field => $def) { foreach ($schema as $field => $def) {
if (array_key_exists($field, $record)) continue; if (array_key_exists($field, $record)) continue;
if (!is_array($def) || ($def['type'] ?? '') !== 'date') continue; if (!is_array($def) || ($def['type'] ?? '') !== 'date') continue;
if (!isset($requiredCols[$field])) continue;
$setParts[] = "`$field` = ?"; $setParts[] = "`$field` = ?";
$params[] = $now; $params[] = $now;
} }
@@ -1661,6 +1672,39 @@ class AcaiAPI
* reintentaba a ciegas. isset(): una web con Db.php personalizado (sin * reintentaba a ciegas. isset(): una web con Db.php personalizado (sin
* lastError) sigue funcionando, solo que sin el detalle. * lastError) sigue funcionando, solo que sin el detalle.
*/ */
/** Cache por peticion: tabla completa => [columna => true]. */
private static $requiredColsCache = [];
/**
* Columnas de `$fullTable` que un INSERT tiene que traer: NOT NULL, sin
* DEFAULT y no auto_increment. Devuelve [columna => true], o null si no se
* pudo consultar (el llamador no rellena nada en ese caso). Solo usa
* Db::executeS, que existe en todas las versiones de Db.php.
*/
private static function requiredColumns($db, $fullTable)
{
if (array_key_exists($fullTable, self::$requiredColsCache)) {
return self::$requiredColsCache[$fullTable];
}
try {
$rows = $db->executeS(
"SELECT COLUMN_NAME FROM information_schema.COLUMNS"
. " WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?"
. " AND IS_NULLABLE = 'NO' AND COLUMN_DEFAULT IS NULL"
. " AND EXTRA NOT LIKE '%auto_increment%'",
[$fullTable]
);
} catch (\Throwable $e) {
return self::$requiredColsCache[$fullTable] = null;
}
$cols = [];
foreach ((array) $rows as $row) {
$name = is_array($row) ? ($row['COLUMN_NAME'] ?? reset($row)) : null;
if ($name) $cols[$name] = true;
}
return self::$requiredColsCache[$fullTable] = $cols;
}
private static function dbErrorSuffix($db) private static function dbErrorSuffix($db)
{ {
return (is_object($db) && isset($db->lastError) && $db->lastError !== '') return (is_object($db) && isset($db->lastError) && $db->lastError !== '')