Skip to content

ADI tag directory: prepend (SAP genuine) vs append (our e511310) — divergence + proposal for a native inspection tool #166

Description

@russimicro

OpenADS — Divergencia de formato ADI con Advantage Data Architect genuino (SAP): orden de tags prepend vs append, y propuesta de herramienta propia

Campo Valor
Proyecto FiveTechSoft/OpenADS (drop-in de Advantage Client Engine)
Reporta Russoft / Zerus ERP (Harbour 3.2 + rddads + OpenADS x64)
Fecha 2026-08-09
Severidad Baja/cosmética para OpenADS mismo (lee sus propios índices sin problema) — alta si algún usuario valida sus datos con el Advantage Data Architect genuino de SAP
Tipo No es un bug de corrupción. Es una divergencia de convención de formato + una propuesta de diseño

1. Resumen ejecutivo

Al crear un tag nuevo en el directorio de tags de un .ADI, hay dos convenciones posibles:

  • prepend (anteponer): el tag nuevo pasa a la posición 0, empuja a los existentes.
  • append (agregar al final): el tag nuevo se agrega después de los existentes.

Confirmamos, con datos creados por el Advantage Data Architect genuino de SAP (no por
OpenADS), que SAP usa prepend. OpenADS, desde el commit e511310 ("tag ordinals follow
creation order, as CDX does", mergeado a nuestro fork junto con la actualización a v1.8.68),
usa append.

Consecuencia: cuando el Advantage Data Architect genuino abre un .ADI escrito por OpenADS
(en modo append), su validación interna —que evidentemente asume prepend— lo reporta como
"duplicate index name", aunque el archivo no está roto: el propio OpenADS lo lee
perfecto (0 nombres duplicados, todos los tags accesibles).

2. Evidencia

Se creó prueba1.adt/.adi con el Advantage Data Architect genuino (no con OpenADS): 1 tag
inicial (header en página 3), se creció la tabla a 867 páginas vía
INSERT INTO ... SELECT * FROM ... repetido, y se creó un segundo tag ahí —su header cae en
página 867 (> 255), el caso que hacía falta para descartar un problema de ancho de campo.

Byte a byte, el directorio de tags (prueba1.adi, página 2, offset 24, entradas de 6 bytes):

tag#0  bytes=63 03 00 00 00 7a   XX(u32 LE)=867   <- el tag NUEVO (2do creado)
tag#1  bytes=03 00 00 00 00 79   XX(u32 LE)=3      <- el tag ORIGINAL (1ro creado)

Dos lecturas:

  1. La codificación u32 LE del offset de página es correcta y compatible — no es un problema de
    ancho de campo (nuestro fix b8eecbf, "keep every tag when the bag grows past page 255",
    sigue siendo válido).
  2. El orden de las entradas está invertido respecto a lo que hoy escribe OpenADS: SAP puso
    el tag nuevo en la posición 0 (prepend), OpenADS (post e511310) lo habría puesto en la 1
    (append).

3. La tensión de diseño (por qué no es un revert trivial)

e511310 no fue capricho: corrigió un bug real reportado por nosotros. Con prepend, una
aplicación que navega órdenes por númeroOrdSetFocus(n) / OrdName(n), que es
exactamente lo que hace un browse con click-to-sort de columna— activa el orden equivocado, y
no puede compensarlo sin saber de antemano cuántos tags va a terminar teniendo la bolsa. Sobre
un .CDX la misma aplicación obtiene el orden de creación (CDX sí es append de forma nativa).

Revertir a prepend (lo probamos en un branch local, con tests actualizados y suite 100 %
verde) es correcto para paridad con SAP, pero rompe cualquier consumidor que dependa de
OrdSetFocus(NÚMERO) fijo
sobre una tabla ADI existente, salvo que ese consumidor resuelva
el número vía el nombre del tag (OrdNumber(nombre)) en vez de un literal. En nuestro caso
concreto eso no es inmediato: buena parte de los tags de producción (los generados por nuestro
propio indexador sin TAG explícito) no tienen nombre semántico — ADS los autonombra ORD9,
ORD10, etc. por posición, así que "el nombre" hoy es la posición, y cambiar el orden
físico los vuelve a barajar igual, con o sin el fix.

4. Lo que decidimos de nuestro lado (por si sirve de contexto)

No vamos a perseguir la paridad byte-a-byte con SAP en este momento: el driver se queda en
modo append (el mismo comportamiento que ya tenía nuestra producción antes de esta
actualización, cero cambios en la aplicación), y dejamos de usar el Advantage Data Architect
genuino para inspeccionar estos archivos — es una herramienta externa de diagnóstico que nunca
usa el ERP en producción, así que el falso "duplicate index name" no lo ve ningún usuario real.

5. Propuesta: un "Architect" propio de OpenADS, en vez de perseguir el formato privativo

Creemos que perseguir paridad exacta con el Advantage Data Architect genuino tiene un techo
bajo: es una herramienta comercial cerrada, su validación interna no está documentada, y ya
encontramos una convención (prepend) que no coincide con lo que el propio equipo de OpenADS
decidió por una razón válida (e511310). Es razonable que en el futuro aparezcan más
divergencias de esta familia, cada una exigiendo elegir entre "compatible con SAP" o
"compatible con lo que ya escribimos".

En vez de perseguir ese blanco móvil, proponemos que el proyecto ofrezca su propia
herramienta de inspección/administración de índices
(algo como openads-architect: listar
tags de un .ADI/.CDX, detectar nombres duplicados de verdad, validar la integridad del
árbol, y opcionalmente reparar/reindexar) que entienda el formato que el propio motor
escribe
, sin intermediar la validación interna de una herramienta de terceros. Beneficios:

  • Los usuarios de OpenADS dejan de depender de una licencia/instalación de Advantage Data
    Architect genuino solo para verificar la salud de sus índices.
  • El comportamiento a validar es el que el propio proyecto define y documenta (como con
    e511310), no el que un tercero infiere de un binario cerrado.
  • Sirve como arnés de regresión natural para cualquier cambio futuro al formato ADI/CDX.

No es un pedido urgente — lo dejamos documentado con la evidencia completa por si en algún
momento el equipo quiere retomar la paridad con SAP, o directamente construir esta herramienta.
El fix a prepend que probamos (AdiIndex::add_tag(), con sus dos tests actualizados,
abi_adi_tagdir_order_test.cpp y abi_adi_native_estaelec_test.cpp) sigue disponible si hace
falta como punto de partida.

6. Referencia cruzada

Relacionado con los reportes previos de la misma serie:
OPENADS_PR_SEEK_PARCIAL_DELETED.md (PR #154) y
OPENADS_ISSUE_refilter_parity.md / PR #155 (scope parcial).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions