From bcd21dd2748da8b1c91859d853898ba73674a671 Mon Sep 17 00:00:00 2001 From: Jordan Diaz Date: Tue, 21 Jul 2026 18:29:46 +0000 Subject: [PATCH] perf: batch per-record translation preload in t() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit t() issued one SELECT per translated field (1,300+ queries on a typical multilanguage home). It now preloads all translations of a (table, record, language) in a single query and serves subsequent fields from memory, preserving the previous LIMIT 1 semantics (first row per fieldName). Measured on villagrancanaria: cold render -60% (en home) / -67% (es home), byte-identical output. REQUIRES index (tableName, recordNum, prefix) on cms_traducciones — without it the batched query scans and is SLOWER than before: ALTER TABLE cms_traducciones ADD INDEX idx_tabla_registro_prefijo (tableName, recordNum, prefix); --- cms/lib/viewer_functions.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/cms/lib/viewer_functions.php b/cms/lib/viewer_functions.php index b1336f4..d66c7a7 100755 --- a/cms/lib/viewer_functions.php +++ b/cms/lib/viewer_functions.php @@ -182,7 +182,23 @@ function t($record, $campo = "title", $options = array(), $idioma=null){ if (@$record["urlPath"] || @$record["info1"]){ $recordtr = $DB->fetch("SELECT fieldValue FROM {$TABLE_PREFIX}traducciones WHERE tableName='uploads' AND recordNum=? AND fieldName=? AND prefix=? AND uploadNum=? LIMIT 1", [$record['recordNum'], $campo, @$idioma, $record['num']]); }else{ - $recordtr = $DB->fetch("SELECT fieldValue FROM {$TABLE_PREFIX}traducciones WHERE tableName=? AND recordNum=? AND fieldName=? AND prefix=? LIMIT 1", [@$record['tableName'], @$record['num'], $campo, @$idioma]); + // Precarga por REGISTRO: una unica query trae todas las traducciones + // del (tabla, num, idioma) y las siguientes llamadas a t() sobre el + // mismo registro salen de memoria. Sin esto, cada campo traducido + // lanzaba su propio SELECT (1.300+ queries en una home tipica). + // Semantica identica al SELECT ... LIMIT 1 anterior: se conserva la + // primera fila por fieldName. + global $__traduccionesPorRegistro; + $__trKey = @$record['tableName']."|".@$record['num']."|".$idioma; + if (!isset($__traduccionesPorRegistro[$__trKey])){ + $__trRows = $DB->executeS("SELECT fieldName, fieldValue FROM {$TABLE_PREFIX}traducciones WHERE tableName=? AND recordNum=? AND prefix=?", [@$record['tableName'], @$record['num'], $idioma]); + $__trMap = array(); + foreach ((array)$__trRows as $__trRow){ + if (!isset($__trMap[$__trRow['fieldName']])) $__trMap[$__trRow['fieldName']] = $__trRow['fieldValue']; + } + $__traduccionesPorRegistro[$__trKey] = $__trMap; + } + $recordtr = isset($__traduccionesPorRegistro[$__trKey][$campo]) ? array("fieldValue" => $__traduccionesPorRegistro[$__trKey][$campo]) : null; } if ($recordtr) { $trad = CocoParser::parsea_codigos_en_linea(parsea_texto2($recordtr["fieldValue"]),$options);