Mise à jour v1.10Ag
Version majeure de stabilisation de l'ORM. Elle corrige 2 bugs critiques
(risque de compilation, hydratation Mode 3 + jointures), refond le traitement des tables système
pour les rendre indépendantes du remapping runtime, sécurise le verrou applicatif en Mode 3, harmonise le trio
transactionnel Begin / Commit / Rollback, introduit un cache global d'introspection
qui résout un bug d'incohérence sur les instances multiples d'une même classe, et corrige un grand nombre de
régressions ponctuelles sur la persistance, le chargement et la gestion d'état ORM. Compatible
descendante sur l'API publique : remplacer le composant, recompiler, déployer.
Cette version regroupe 46 modifications consolidées depuis la v1.00I. Synthèse par catégorie :
| Catégorie | Volume | Impact global |
|---|---|---|
| 🔴 Critique | 2 | Risque de non-compilation et hydratation framework cassée en Mode 3 + jointures |
| 🏛️ Tables système | 3 | Approche C : les tables système (cshx2_*) ignorent désormais le remapping runtime des colonnes framework |
| 🔒 Verrou applicatif | 3 | Verrou opérationnel en Mode 3 sur tables système ; contrôle d'intégrité HNbEnr > 1 |
| 💼 Trio transactionnel | 2 | Pattern owner sécurisé sur Begin, Commit, Rollback + signatures de retour + symétrie providers |
| 🧠 Cache d'introspection | 3 | Cache global g_tabDéfinitionsClasses — résout l'incohérence sur instances multiples d'une même classe |
| 🔧 Persistance & chargement | ~12 | Corrections sur prv_SQL_Persist, mth_ChargerSelonClauseWhere, mth_RAZ, prv_SQL_BuildValues… |
| 🔐 Crypto | 1 | pub_Encrypt / pub_Decrypt — retour en chaîne simple (annulation refacto C07) |
| 🐛 Fixes mineurs | ~14 | Violations R6 (init non-littérale), STOP en 1ère instruction, libellés erronés, etc. |
| 🧹 Nettoyage | 4 | Variables globales orphelines, procédures jamais appelées, vestiges de nommage |
Pour le détail exact de chaque modification (procédures touchées, extraits de code, périmètre, justifications), voir la section 12 (Détail technique) qui récapitule les 46 cartes du board Trello ORM2026 → ChangeLog.
02.a — p_ClasseDéfinition déclarée 2 fois (risque de compilation)
🚨 Bloquant compilation
Dans le fichier de classe xFrameWork_CSHX2, la propriété p_ClasseDéfinition
était déclarée deux fois — vestige d'un refactor où une déclaration avait été ajoutée
sans repérer celle existante. Selon la tolérance de l'éditeur WinDev, cela pouvait soit générer une
erreur de compilation bloquante, soit produire un comportement indéfini quant à la déclaration
effectivement utilisée.
02.b — Mode 3 + jointures : 11 membres framework non hydratés
🚨 Hydratation casséeEn Mode 3 (METADATA_MODE_JOIN) avec jointures héritées,
les 11 membres framework (m_sSQL_UUID, m_dhSQL_INSERTED,
m_bSQL_LOCKED, etc.) n'étaient pas hydratés — ni sur l'objet racine, ni sur les instances
de m_tabResults, ni sur les sous-objets joints.
Cause racine : prv_CLASS_Mapping cherchait dans
g_tabListesMembres sur sRubriqueSQL, alimenté par
..AttributMapping. Or, en Mode 3, les colonnes framework sont déclarées
sans <Mapping> côté analyse WDD — l'introspection les sautait
(SI MembresClasse..AttributMapping = "" ALORS CONTINUER FIN) et l'écriture dans
m_tabResults était silencieusement skippée.
- Détection de verrous cassée :
m_bSQL_LOCKED = Fauxmême siSQL_LOCKED = Vraien base — un autre poste ne voyait pas le verrou. - Audit cassé :
m_sSQL_USER_INSERT,m_dhSQL_INSERTEDet leurs équivalents UPDATE non hydratés en mémoire. - UUID indisponible côté objet alors que présent en base.
prv_SQL_SelectJoinAnalysis · procédure interne TraiterMetadataLes 11 colonnes framework (SQL_UUID, SQL_LOCKED, SQL_INSERTED…)
peuvent être remappées à runtime via stConfig.MetadataColumns (par exemple
SQL_UUID → mon_uuid_custom). Ce remapping est légitime sur les
tables métier mais illégitime sur les tables système
(cshx2_infosystem, cshx2_archives, cshx2_traductions,
cshx2_trashcan, cshx2_metadata) qui doivent toujours utiliser les noms
canoniques.
La classe xFrameWork_CSHX2_DataBase appliquait systématiquement les constantes runtime
lors de la création / modification / rétro-ingénierie des colonnes framework, ce qui entraînait deux
bugs :
- Création de colonnes sous le nom remappé sur les tables système.
DROP COLUMNerroné sur des colonnes framework canoniques physiquement présentes sur une table système quand le remapping était actif.
- Nouvelle procédure privée
prv_IsSystemTablecentralisant la détection des tables système. prv_InjectFrameworkColumnsetprv_ReverseEngineerTableaiguillent désormais sur cette détection : noms canoniques sur table système, noms remappés sur table métier.prv_GetMembreByFieldName(xFrameWork_CSHX2) bénéficie d'un fallback table système pour résoudre correctement les colonnes framework remappées.prv_ResolveFrameworkPhysicalNameest promu en méthode de classe et appliqué à 2 sites fautifs (InsertBatchCore,SelectRequestBuilder).
En Mode 3 (METADATA_MODE_JOIN), les procédures de verrou applicatif lisaient
SQL_LOCKED / SQL_IP_USER via LEFT JOIN cshx2_metadata.
Or les tables système (cshx2_infosystem, cshx2_archives,
cshx2_traductions, cshx2_trashcan) ne génèrent jamais d'entrée dans
cshx2_metadata par design. Conséquence : le LEFT JOIN ramenait toujours NULL,
SQL_LOCKED apparaissait à 0, et le verrou applicatif était silencieusement
contourné. Deux postes pouvaient verrouiller en parallèle sans conflit détecté — corruption silencieuse
des données i18n possible sur cshx2_traductions.
Les procédures mth_LockSelonID et
mth_UnLockSelonID testent désormais
m_nMetaDataMode = METADATA_MODE_JOIN _ET_ PAS :prv_IsSystemTable().
Sur table système, la lecture / écriture du verrou se fait directement sur la table métier via
prv_GetPhysicalColumnName (noms canoniques garantis).
Le SELECT FOR UPDATE du verrou peut, pour une PK donnée, remonter plusieurs lignes
dans deux scénarios :
- Mode 3 (table applicative) : LEFT JOIN
cshx2_metadataproduisant des doublons (incohérence des metadata). - Mode 1/2 ou table système (lecture directe) : violation d'unicité physique de PK (corruption schéma).
Dans les deux cas, poursuivre la pose ou la libération du verrou sur un état corrompu est dangereux.
Un contrôle HNbEnr > 1 est ajouté en sortie de HLitPremier et fait échouer
la procédure avec ERR_ORM_LOCK_INTEGRITE. Transaction locale rollbackée. Aucune écriture.
Revue spontanée du trio mth_TransactionBegin /
mth_TransactionCommit / mth_TransactionRollback
de la classe xFrameWork_CSHX2_Transactions. Plusieurs problèmes cumulés étaient présents :
pattern owner fragile, audit désaligné, branches providers asymétriques, signatures de retour absentes,
AUTRES CAS au lieu de AUTRE CAS.
m_sInitialCaller était assigné directement à sTraitementAppelant, qui
pouvait être vide. Conséquence : n'importe quel appel à Commit("") ou
Rollback("") pouvait fermer une transaction qui n'était pas la sienne.
- Fallback
dbgInfo: si l'appelant ne fournit pas son nom, auto-détection viadbgInfo(dbgTraitement, dbgTraitementAppelant)garantit unm_sInitialCallernon vide. - Audit aligné :
m_sCurrentProcedureetm_sInitialCallerpartagent désormais le même fallback. - Symétrie providers :
CommitetRollbackacceptent désormais les mêmes branches queBegin(plus de transaction ouvrable mais non fermable). - Trace MySQL/MariaDB conditionnée à la réussite de
SQLTransaction. - Signatures de retour ajoutées :
BeginetCommitrenvoient explicitement(booléen, entier, chaîne).Rollbackconserve unRENVOYERconditionné parEnModeTest()(choix projet documenté en en-tête). AUTRES CAS→AUTRE CAS(règle 43 du skill WLangage) corrigé sur les 3 procédures.ALORSmanquant sur une condition deRollback: ajouté.
| Carte | Sujet |
|---|---|
| Sxo78Svv | mth_TransactionBegin — Pattern owner sécurisé + signature retour + symétrie providers |
| KGzZxfmo | mth_TransactionCommit + mth_TransactionRollback — Signature retour, AUTRE CAS, ALORS manquant, en-têtes |
| ff7ZCoJN | mth_ChargerVersFormulaire — Lecture seule sans verrou ni transaction (paradoxe code/commentaire + transaction inutile) |
pro_CLASS_Introspection alimentait deux choses : (1) un état global
(g_tabListesMembres, tri dicho, cache) — une fois par classe ; (2) une donnée par instance,
:ORM_CSHX2.ClasseDéfinition. Le garde pub_NeedsIntrospection()
testant le cache global retournait Faux au 2ᵉ passage → pro_CLASS_Introspection
n'était pas appelée → :ORM_CSHX2.ClasseDéfinition jamais alimentée sur les
instances 2+ d'une même classe.
Symptôme runtime : :p_ClasseDéfinition.Nom = "" sur toutes les instances
d'une classe sauf la première.
Une Définition est immuable à l'exécution — elle dépend du code compilé,
pas de l'état runtime. Stocker une copie par instance est inutile. Solution retenue : remplacer le stockage
par-instance :ORM_CSHX2.ClasseDéfinition par un cache global indexé par nom de classe
g_tabDéfinitionsClasses (tableau associatif de Définition), alimenté une seule fois
par classe et lu par toutes les instances via p_ClasseDéfinition.
pro_CLASS_Introspection, p_ClasseDéfinition, prv_META_CheckMembersLa v1.00I avait introduit dans prv_SQL_Persist un bloc de clonage qui
ajoutait automatiquement l'instance courante dans m_tabResults après tout INSERT unitaire
réussi, pour aligner le comportement INSERT sur celui du SELECT
(p_nOccurrencesTrouvées = 1 immédiatement après mth_Enregistrer).
Le clonage Sérialise/Désérialise est structurellement instable dans ce contexte.
Toutes les stratégies testées échouent :
TableauAjouteLigne(tab, objet)direct — erreur 1272 (chevauchement mémoire)Sérialise/Désérialise psdBinairevia indirection — erreur 2411 (matching case-sensitive sur nom de classe)Sérialise/Désérialisevia objet dynamique — erreur 2602 (sérialisation indisponible)
Le bloc de clonage est retiré de prv_SQL_Persist.
Conséquence : après un INSERT unitaire, m_tabResults n'est plus auto-alimenté. Le code applicatif
doit appeler mth_ChargerSelonID ou
mth_ChargerSelonClauseWhere s'il a besoin de l'objet hydraté dans
m_tabResults juste après l'INSERT.
Les procédures pub_Encrypt et pub_Decrypt
avaient été refactorées (marqueur C07) pour renvoyer un tuple
(booléen, entier, chaîne, chaîne) afin de remonter une erreur en cas de provider non supporté.
Les 4 appelants de pub_Decrypt dans xFrameWork_CSHX2
(1 dans ConstruitRequête, 3 dans prv_SQL_TranslationBuilder)
concaténaient le retour comme une chaîne simple. WLangage prenait alors implicitement la première valeur
du tuple (le booléen) et l'insérait dans le SQL, produisant des fragments type
"1 AS alias" au lieu de "AES_DECRYPT(...) AS alias".
La branche AUTRE CAS de pub_Encrypt/Decrypt
est inatteignable en pratique : la connexion est établie en amont avec un provider
supporté (MySQL · MariaDB · PostgreSQL) et des dizaines de SELON gcnxDataCshX2..Provider
en amont l'ont déjà validé. La protection tuple était du sur-engineering qui cassait tous les sites
appelants. Retour à la signature chaîne simple.
Plusieurs corrections sur la gestion d'état des instances ORM, le cycle des clauses SELECT/WHERE et le
chargement par mth_ChargerSelonClauseWhere. Quelques régressions ont été
introduites puis corrigées au cours du cycle, voir tableau.
mth_RAZ: extension du reset aux flags d'état, clauses SELECT et batches — séparation stricte état (à reset) vs configuration (préservée).- Garde première erreur :
mth_RAZen post-boucle conditionné au succès, pour ne pas écraser l'erreur initiale en cas d'incohérence d'objet. mth_ChargerSelonClauseWhere: RAZ totale des 9 clauses enFIN:(cycle pose / RAZ / revert documenté ci-dessous).mth_ChargerSelonID: passage explicite des paramètres pour fixer la régression où*écrasait la clause WHERE.mth_EnregistrerTableau: test d'optimisation UPDATE corrigé (m_bUpdatedinexistant →p_bUpdated).mth_RenvoyerAttributSelonGUID: critère de recherche corrigé (ORM_CSHX2.sGUIDTemp) et indirection avecindVariable.prv_DetectFrameworkMissingColumns: RAZ ciblée + ajout dans la bonne clé (fix bug multi-classes + bug d'indexation).
| Carte | Sujet |
|---|---|
| 3UYMQTJk | mth_ChargerSelonClauseWhere — Pose directe des paramètres + RAZ totale en FIN: (introduit la régression *) |
| SLw4dAcN | mth_ChargerSelonClauseWhere — RAZ uniquement en cas de succès (préservation pour diagnostic) |
| ooi6qrH1 | mth_ChargerSelonID — Passage explicite des paramètres (fix régression 3UYMQTJk) |
| lfFQFiNF | mth_ChargerSelonClauseWhere — REVERT pose inconditionnelle (rollback 3UYMQTJk, retour au comportement historique) |
| R5QjtLpD | mth_RAZ — Extension du reset aux flags d'état, clauses SELECT et batches (séparation état/configuration) |
| 4exiIykx | mth_RAZ — Reset post-boucle conditionné au succès + garde première erreur |
| P5XAKb7f | mth_EnregistrerTableau — Correction du test d'optimisation UPDATE (m_bUpdated → p_bUpdated) |
| C6rPyYTe | mth_RenvoyerAttributSelonGUID — Critère de recherche corrigé + indirection |
| fUz6H8uu | prv_DetectFrameworkMissingColumns — RAZ ciblée + ajout dans la bonne clé |
Cette catégorie regroupe les corrections de violations du skill WLangage (initialisations par expression
non-littérale, déclarations inline interdites, STOP en première instruction, libellés erronés…)
et le nettoyage de vestiges (variables globales orphelines, procédures jamais appelées, commentaires obsolètes).
prv_SQL_BuildValues·prv_InjectFrameworkValues·prv_SQL_InsertCore— Oubli dehRubDate6,Majusculemanquant, recherches vides enMETADATA_MODE_SQL_ONLYprv_SQL_UpdateCore— 3 initialisations par expression non-littérale (violation R6/3.2)prv_FormatFrameworkValue— Risque de récursion infinie sur colonne inconnue (CAS 0 sans filet)ConstruitRequête—bRubriqueTraduitedéclarée inline dans unCAS(violation R6)mth_RenvoyerIDSelonAttribut—STOPen 1ère instruction + titre d'en-tête fauxConstructeur— Vestige commentaire:prv_CLASS_Introspection()obsolèteORM_AddTranslation— Ajout en-tête + renommagesSQLFiled→sSQLField
g_tabListeRubriquesAnalyse— Variable globale alimentée mais jamais lue → suppriméeprv_MapFrameworkColumns— Procédure jamais appelée → suppriméeprv_SQL_SelectJoinAnalysis— Procédure interneRechercheIDNonSignéjamais appelée → suppriméeprv_SQL_DeleteCascade— Variable localesCléSynchronisationjamais lue → supprimée
La liste exhaustive (avec lien Trello) est disponible en section 12 (Détail technique).
| Axe | Statut |
|---|---|
| Providers | MySQL · MariaDB · PostgreSQL |
| Modes metadata | Standard · SQL_ONLY · JOIN |
| Rétrocompatibilité API publique | Totale |
Comportement m_tabResults après INSERT unitaire | Changement — l'auto-alimentation introduite en v1.00I est retirée. Voir section 07. |
Signature pub_Encrypt / pub_Decrypt | Changement — retour à la chaîne simple (annulation C07). Voir section 08. |
Stockage de la Définition de classe | Changement — passage de :ORM_CSHX2.ClasseDéfinition (par instance) au cache global g_tabDéfinitionsClasses. Lecture via p_ClasseDéfinition inchangée. Voir section 06. |
| Mise à jour base de données | Aucune |
| Action requise côté code applicatif | Vérifier le code applicatif qui dépendait de m_tabResults auto-alimenté après mth_Enregistrer (cas rare). Recompiler, déployer. |
Mise à jour fortement recommandée pour tous les déploiements en production
utilisant le Mode 3 (METADATA_MODE_JOIN) ou des tables système avec
remapping runtime des colonnes framework — les corrections critiques de cette version
résolvent des cas où des verrous applicatifs étaient silencieusement contournés et où des membres
framework n'étaient pas hydratés en mémoire.
Liste exhaustive des cartes intégrées dans cette version, regroupées par catégorie et avec lien vers le détail Trello (procédures touchées, extraits de code, périmètre précis, justifications).
| Carte | Sujet | |
|---|---|---|
| 🔴 Critique | ||
| dbmdUXee | xFrameWork_CSHX2 — p_ClasseDéfinition déclarée 2 fois (risque compilation) | |
| CpmYRXCc | TraiterMetadata — Mode 3 + jointures : 11 membres framework non hydratés (F3) | |
| 🏛️ Tables système | ||
| L3hlpu1p | prv_IsSystemTable + prv_InjectFrameworkColumns + prv_ReverseEngineerTable — Approche C : tables système vs remapping runtime | |
| z2v2Odmq | prv_GetMembreByFieldName — Fallback table système pour résolution des colonnes framework remappées | |
| gKYlqCNT | prv_ResolveFrameworkPhysicalName — Promotion en méthode de classe + correction de 2 sites fautifs (InsertBatchCore, SelectRequestBuilder) | |
| 🔒 Verrou applicatif | ||
| nzE2m8PI | C2 — Verrou applicatif inopérant en Mode 3 sur tables système (Lock / UnLock / DeleteTranslations) | |
| lbfVn72r | mth_LockSelonID / mth_UnLockSelonID — Contrôle d'intégrité HNbEnr > 1 sur SELECT FOR UPDATE | |
| klgbk6At | mth_EffacerSelonID — Libellés "mth_LockSelonID" au lieu de "mth_EffacerSelonID" | |
| 💼 Trio transactionnel | ||
| Sxo78Svv | mth_TransactionBegin — Pattern owner sécurisé + signature retour + symétrie providers | |
| KGzZxfmo | mth_TransactionCommit + mth_TransactionRollback — Signature retour, AUTRE CAS, ALORS manquant, en-têtes | |
| ff7ZCoJN | mth_ChargerVersFormulaire — Lecture seule sans verrou ni transaction (paradoxe code/commentaire + transaction inutile) | |
| 🧠 Cache d'introspection | ||
| P7yY0JAR | pro_CLASS_Introspection + p_ClasseDéfinition — Cache global g_tabDéfinitionsClasses (fix ClasseDéfinition vide sur instances 2+ d'une même classe) | |
| iS6W3uvh | prv_META_CheckMembers — Bascule sur g_tabDéfinitionsClasses (conséquence de la suppression de ORM_CSHX2.ClasseDéfinition) | |
| CQG1tiBV | Renommage bPartiallyHydrated → p_bPartiallyHydrated (alignement convention framework, fix erreur runtime 1059) | |
| 🔧 prv_SQL_Persist | ||
| A2LVnWpA | prv_SQL_Persist — Correction erreur 1272 sur ajout instance dans m_tabResults (copie membre par membre) | |
| SLWPmZia | prv_SQL_Persist — Clone par sérialisation binaire + drapeau bPartiallyHydrated | |
| uiRZ3Zvv | prv_SQL_Persist — RETRAIT du clonage dans m_tabResults après INSERT unitaire (rollback partiel de mtIMs9Nx : Sérialise/Désérialise structurellement instable) | |
| 🔐 Crypto | ||
| PZ5RCqfu | pub_Encrypt / pub_Decrypt — Retour en chaîne simple (annulation refacto C07, 4 appelants deviennent corrects) | |
| 📦 État ORM & clauses | ||
| 3UYMQTJk | mth_ChargerSelonClauseWhere — Pose directe des paramètres + RAZ totale des 9 clauses en FIN: (persistance clauses + code mort + commentaire obsolète) | |
| SLw4dAcN | mth_ChargerSelonClauseWhere — RAZ des clauses uniquement en cas de succès (préservation pour diagnostic sur échec) | |
| ooi6qrH1 | mth_ChargerSelonID — Passage explicite des paramètres à mth_ChargerSelonClauseWhere (fix régression 3UYMQTJk : * écrasait la clause WHERE) | |
| lfFQFiNF | mth_ChargerSelonClauseWhere — REVERT pose inconditionnelle des clauses (rollback 3UYMQTJk, retour au comportement conditionnel historique) | |
| R5QjtLpD | mth_RAZ — Extension du reset aux flags d'état, clauses SELECT et batches (séparation état/configuration) | |
| 4exiIykx | mth_RAZ — Reset post-boucle conditionné au succès + garde première erreur (incohérence objet après erreur de type) | |
| P5XAKb7f | mth_EnregistrerTableau — Correction du test d'optimisation UPDATE (m_bUpdated inexistant → p_bUpdated) | |
| C6rPyYTe | mth_RenvoyerAttributSelonGUID — Critère de recherche corrigé (ORM_CSHX2.sGUIDTemp) + indirection avec indVariable | |
| fUz6H8uu | prv_DetectFrameworkMissingColumns — RAZ ciblée + ajout dans la bonne clé (bug multi-classes + bug d'indexation) | |
| 🐛 Fixes mineurs | ||
| j0HMygo5 | prv_SQL_BuildValues — hRubDate6 + Majuscule manquant + recherche vide SQL_ONLY | |
| 6NEM3Ed8 | prv_InjectFrameworkValues — Recherche vide en Mode SQL_ONLY (colonnes framework jamais injectées) | |
| oHgCX40s | prv_FormatFrameworkValue — Récursion infinie potentielle CAS 0 (filet manquant) | |
| 0DX6SgYY | prv_SQL_UpdateCore — 3 initialisations par expression non-littérale (violation R6/3.2) | |
| PnKijNkv | ConstruitRequête — Déclaration "bRubriqueTraduite" inline dans un CAS (violation R6) | |
| GWPDQC7E | Constructeur — Vestige commentaire ":prv_CLASS_Introspection()" obsolète | |
| QsR73nXP | prv_SQL_BuildValues — Oubli hRubDate6 + Majuscule manquant + Recherche vide Mode SQL_ONLY | |
| NpWrK76v | prv_InjectFrameworkValues — Recherche vide sur colonnes "manquantes" en Mode SQL_ONLY | |
| BpXy4aWZ | prv_SQL_InsertCore — Oubli de hRubDate6 dans la branche "valeur par défaut" | |
| S5EheH9B | mth_RenvoyerIDSelonAttribut — STOP en 1ère instruction + titre d'en-tête faux | |
| X9O0DNkp | prv_SQL_UpdateCore — 3 init par expression non-littérale à la déclaration (violation R6) | |
| NjidxFhW | ConstruitRequête — bRubriqueTraduite déclarée inline dans un CAS (violation R6) | |
| afbFOAGn | prv_FormatFrameworkValue — Risque récursion infinie sur colonne inconnue (CAS 0) | |
| GyEbvRGK | Constructeur — :prv_CLASS_Introspection() commenté (vestige nommage obsolète) | |
| uF86W5Jy | ORM_AddTranslation — Ajout entête + renommage sSQLFiled → sSQLField | |
| 🧹 Nettoyage | ||
| Si2Jenc9 | g_tabListeRubriquesAnalyse — Variable globale alimentée jamais lue | |
| Aftio1BN | prv_MapFrameworkColumns — Procédure jamais appelée | |
| 8LC0MEyY | prv_SQL_SelectJoinAnalysis — Procédure interne RechercheIDNonSigné jamais appelée | |
| vgszeIGw | prv_SQL_DeleteCascade — Variable locale sCléSynchronisation jamais lue | |