martes, 1 de julio de 2008

CÓDIGO SQL EMBEBIDO


... la verdad es que pensaba titular este artículo así : 'Solo para valientes', pero no era demasiado descriptivo, ¿ o sí ?. En cualquier caso se trata de un tema solo recomendable a expertos en lenguaje sql, pues es fácil cometer errores que impidan el correcto funcionamiento de este sistema. Además, advierto que se debe proteger/controlar/verificar (mediante un formulario y el código que sea preciso) la 'bondad' de las reglas que se van a embeber. De no actuar así, estamos dejando una puerta abierta a que un usuario malintencionado nos haga un estropicio.

Para explicar de qué se trata, quizás sea mejor mediante un ejemplo : Supongamos que deseamos mostrar, mediante una consulta, los mensajes de correo electrónico que tenemos guardados en una tabla 'Mensajes'. Pero, imitando a los programas de correo, queremos filtrar el contenido, o el título, o el remitente de dichos mensajes. Para ello, hemos creado una segunda tabla 'Reglas', donde pensamos guardar expresiones que actúen como regla de validación de los correos. Es decir, queremos guardar fragmentos de código sql, dentro de una tabla y después ejecutar estos trozos de código dentro de una consulta.
Si habéis intentado algo por el estilo desde vba, ya sabréis que directamente no se puede, y que es necesario emplear 'un intermediario' que explique al compilador que una cadena de texto, no es tal, sino un fragmento de código. En este ejemplo hemos usado la función Eval() como intérprete de nuestros deseos. Sin embargo surge un problema con ello : para que este 'invento' tenga utilidad, es necesario, imprescindible, poder pasar valores por referencia, desde fuera de la regla, adentro de la misma. En concreto necesitamos que si en la regla escribimos el nombre de un campo de la tabla 'Mensajes', obtengamos el valor de dicho campo. Lamentablemente esto, en principio, no es posible, pero ... quizás es el momento de ver una secuencia (un lote) ejecutable desde el 'Editor Sql 1.1' que nos muestre cómo :

Dos advertencias previas : He substituido el operador Like y el carácter comodín '*' por ALike y '%'. Ha sido necesario por la forma de trabajar interna del Editor, pero para ejecutarse en una consulta de Access, esto no es necesario (a no ser que, en Opciones, tengamos activada la compatibilidad con Sql Server).
Para usar la función Eval() desde Sql, es necesario bajar el nivel de seguridad de Access.

/* Filtros embebidos, Ramon Poch, raipon. Terrassa a 13/05/2008 */

-- Creamos una tabla de mensajes de correo electrónico
Create Table Mensajes
(
Id Counter Primary Key,
Destinatario Text (255),
Titulo Text (255),
Cuerpo Memo
);

-- Insertamos unos cuantos valores de prueba
Insert Into Mensajes (Destinatario, Titulo, Cuerpo)
Values
("UnaDireccion@UnServidor.com",
"Prueba1",
"Oferta de electrodomesticos de ocasión ..."
);

Insert Into Mensajes (Destinatario, Titulo, Cuerpo)
Values
(
"OtraDireccion@OtroServidor.com",
"Prueba2",
"¿ Cómo te han ido estos dias de vacaciones ..."
);

Insert Into Mensajes (Destinatario, Titulo, Cuerpo)
Values
(
"UnaDireccion@UnServidor.com",
"Prueba3",
"Compra venta de ..."
);

-- Creamos una tabla de reglas
Create Table Reglas (Id Counter Primary Key, Regla Text (255));

-- Insertamos Reglas que sean capaces de restringir los mensajes
Insert Into Reglas (Regla)
Values
(
"'•Destinatario•' ALike '%@TutiSpam%'"
);

Insert Into Reglas (Regla)
Values
(
"'•Titulo•' = 'Gran oferta'"
);

Insert Into Reglas (Regla)
Values
(
"'•Cuerpo•' ALike '%Compra%'
Or
'•Cuerpo•' ALike '%Venta%'
Or
'•Cuerpo•' ALike 'Oferta'"
);

-- Veamos los mensajes ...
Select * From Mensajes;

-- Comprobamos qué efecto tiene el filtro :
Select * From Mensajes
Where Not Exists
(
Select Id From Reglas
Where
Eval(
Replace(
Replace(
Replace(Regla, "•Destinatario•", Destinatario),
"•Titulo•",Titulo),
"•Cuerpo•",Cuerpo)
)=-1
);

/* ¿ Qué hemos hecho ?. Para poder interpretar como código, el texto guardado en el campo Regla, utilizamos la función Eval(), sin embargo, desde dentro de esta función no podemos pasar valores por referencia de los campos 'Cuerpo', 'Titulo' o 'Destinatario' . Para solucionar este inconveniente empleamos la función Replace(), que sí que es capaz de leer por referencia los campos de la consulta y modificar la cadena de texto a interpretar por Eval(). Es decir, és la función Replace() la que 'pasa' el valor del campo a Eval(). Evidentemente es necesario emplear tantas veces la función Replace() embebida, como campos deseemos controlar. El carácter '•' (Alt 7) lo utilizamos para localizar de forma inequívoca el nombre del campo dentro de la regla.*/
-- Eliminamos los objetos del ejemplo
Drop Table Mensajes;
Drop Table Reglas;

En resumidas cuentas, se trata de un método que nos permite añadir funcionalidad a nuestras aplicaciones : los usuarios pueden modificar dinámicamente el comportamiento de algunas consultas, sin necesidad de alterar el diseño de las mismas.

Ramon Poch. Terrassa a 13/05/2008.