NULL en Genero: || lo anula todo, SFMT no
El problema: una cadena que desaparece
Montas un texto juntando campos y, de vez en cuando, sale vacío entero. No le falta un trozo: no queda nada. Casi siempre es un NULL en uno de los operandos de ||.
La causa: NULL contagia toda la expresión
El manual lo dice en dos sitios:
- En la página de
NULL: si un elemento de una expresión es NULL, la expresión entera se evalúa como NULL. - En la del operador
||: si cualquiera de los miembros de una concatenación es NULL, la cadena resultante es NULL.
Hay una tercera regla que agrava la segunda: en Genero, el literal de cadena vacía "" equivale a NULL.
Las cuatro formas de juntar cadenas
MAIN
DEFINE l_nombre, l_apellido, l_texto STRING
LET l_nombre = "Ana"
-- l_apellido se queda en NULL
LET l_texto = l_nombre || " " || l_apellido
DISPLAY "|| [", l_texto, "]"
LET l_texto = l_nombre, " ", l_apellido
DISPLAY "coma [", l_texto, "]"
LET l_texto = SFMT("%1 %2", l_nombre, l_apellido)
DISPLAY "SFMT [", l_texto, "]"
LET l_texto = l_nombre.append(l_apellido)
DISPLAY "append [", l_texto, "]"
LET l_texto = NVL(l_apellido, "")
DISPLAY "NVL(x, \"\") sigue siendo NULL: ", l_texto IS NULL
END MAIN
|| []
coma [Ana ]
SFMT [Ana ]
append [Ana]
NVL(x, "") sigue siendo NULL: 1
||pierde la cadena entera por un solo operando NULL.- La coma (
LET a = b, c) ySFMTtratan el NULL como si no hubiera nada. Ask Reuben (#3) recomiendaSFMTporque la plantilla completa se lee de un vistazo. .append()tampoco devuelve NULL. No modifica la variable: devuelve una cadena nueva que hay que asignar.CALL l_nombre.append(...)no compila, da-6611.NVL(x, "")no arregla nada, porque""es NULL. Si se quiere un valor por defecto, tiene que ser uno que no esté vacío, por ejemploNVL(x, "-").
Un caso real: la línea vacía que no llevaba CR
En el cliente SMTP de la entrada sobre enviar correo, cada línea se escribía con su CRLF así:
CALL ch.writeLine(p_texto || "\r")
Funcionaba en todas las líneas menos en la vacía. linea(ch, "") recibe NULL, NULL || "\r" da NULL, y writeLine escribe solo el salto de línea, sin el CR. Esa línea vacía era la que separa las cabeceras del cuerpo del mensaje. No se ve leyendo el código: apareció en un volcado de los bytes enviados. La solución fue tratar el NULL aparte y escribir "\r" directamente.
Otro caso real: un BOOLEAN que no entra en ningún IF
NULL no solo afecta a las cadenas. Una comparación con un operando NULL da NULL, y un BOOLEAN puede quedarse en NULL sin que se note:
MAIN
DEFINE l_seguir BOOLEAN
LET l_seguir = (arg_val(1) == "seguir")
IF l_seguir THEN
DISPLAY "rama IF"
END IF
IF NOT l_seguir THEN
DISPLAY "rama IF NOT"
END IF
DISPLAY "l_seguir IS NULL: ", l_seguir IS NULL
LET l_seguir = NVL(l_seguir, FALSE)
IF NOT l_seguir THEN
DISPLAY "rama IF NOT, tras NVL"
END IF
END MAIN
Sin argumentos, arg_val(1) devuelve NULL:
l_seguir IS NULL: 1
rama IF NOT, tras NVL
No se imprime ni «rama IF» ni «rama IF NOT». NOT NULL sigue siendo NULL, y un IF con NULL no entra. Este caso salió al escribir el ejemplo de la entrada sobre WHENEVER: el programa tenía que pararse sin argumentos y seguía. Con NVL(l_seguir, FALSE), o con un LET l_seguir = FALSE y un IF que lo ponga a TRUE, el valor queda definido.
Comparar con NULL: IS NULL, nunca ==
x == NULL nunca es verdadero: da NULL. El manual pide usar IS NULL, y el compilador lo recuerda con -W all:
warning:(-6636) Invalid usage of NULL in an expression. Do you mean IS NULL?
Conclusión
En Genero, un NULL en cualquier operando anula toda la expresión, y "" también es NULL. Para concatenar, SFMT o la coma. Para valores por defecto, NVL con un valor que no esté vacío. Y para un BOOLEAN que viene de una comparación, darle un valor explícito antes de usarlo en un IF.
Fuentes
- Manual Genero BDL 6.00: NULL (expresiones con NULL,
""equivale a NULL,IS NULL); operador||(Concatenate);||=. - Ask Reuben #3, Append or Concatenate: https://4js.com/ask-reuben/ig-3/
Qué está comprobado y qué no
Que NULL anula la expresión, que "" equivale a NULL, el comportamiento de || y el uso de IS NULL están contrastados con el manual de Genero 6.00. Que la coma y SFMT no devuelven NULL viene de Ask Reuben #3; la página de SFMT del manual no lo menciona.
Compilado y ejecutado en Genero 5.01.04. Los listados se copiaron tal cual del texto de la entrada:
- Las cuatro formas de concatenar y
NVL(x, ""), con la salida de arriba. - El BOOLEAN a NULL: no entra en ninguna rama, y
NVL(l_seguir, FALSE)lo arregla. CALL l_nombre.append(...)no compila (-6611).- El aviso
-6636al comparar con== NULL.
El caso de la línea vacía se comprobó en un volcado de bytes del cliente SMTP de la entrada sobre correo.
Deja una respuesta