Home Map Index Search News Archives Links About LF
[Top Bar]
[Bottom Bar]
[Photo of the Author]
Luis Colorado
Acerca del Author:

Luis Colorado trabaja como administrador de sistemas UNIX y de acceso a Internet para la empresa Telefónica Sistemas, S.A. en España. Es Licenciado en CC. Físicas por la Universidad Complutense de Madrid y ha preparado varias utilidades de libre distribución para entornos UNIX.

Escribe al autor

Contenidos:
Introducción
Motivación
M4
¿Cómo funciona?
¿Cómo pasa la información del usuario al CGI?
Descarga (Download)

PG2CGI Acceso a Bases de Datos desde el Web

[Ilustration]

Resumen:

En este artículo se describe un programa para permitir el acceso a bases de datos desde consultas HTML. El programa tiene como requisitos el control de accesos, la flexibilidad en la configuración y la independencia del formato de presentación. El programa es el resultado de una serie de ideas surgidas de la lectura de una serie de artículos sobre M4, aparecidos en las revistas Linux Journal y LinuxFocus (www.linuxfocus.org), en los 12 últimos meses.


Introducción

Este artículo pretende ser una descripción de los trabajos ---como dicen los ingleses, `a rationale'--- o de las ideas que me llevaron a realizar este programa.

No pretende ser un manual de usuario (un manual de referencia acompaña al software, cuya URL de obtención se presenta a lo largo del artículo) sino mas bien una presentación del mismo y una invitación a los usuarios a que lo empleen y comuniquen al autor cualquier problema que puedan encontrar con él.

Motivación

El programa surgió como respuesta a una serie de artículos aparecidos en las revistas Linux Journal (*) y LinuxFocus (*) sobre el empleo de M4 en entornos de gestión de documentación HTML, ya que dichos artículos mostraban el potencial de M4, por sí solo, para el mantenimiento de documentos HTML y control de información insertable en el momento de generación de las páginas.

Por otro lado, la disponibilidad de numerosos programas servidores web y numerosas aplicaciones de base de datos, hacía chocante la ausencia de aplicaciones de interfaz entre los dos entornos (la mayoría de aplicaciones de este tipo son comerciales, o tienen grandes defectos respecto a la dependencia de los formatos de presentación).

Este programa trata de unificar los dos mundos con una aplicación que reuna los siguientes requisitos:

La aplicación propuesta consigue de forma aceptable estos requisitos, a costa de un pequeño coste de eficiencia (la aplicación ha de llamar varias veces a M4 en el curso de una ejecución) pero los resultados son satisfactorios en la mayoría de los casos (normalmente, los accesos a bases de datos suelen provocar retardos superiores en las propias consultas).

M4

M4 se creó hace ya mucho tiempo con la idea de tener un procesador de macros potente y totalmente general. La utilización de M4 por la aplicación es muy fuerte:

Esto puede provocar perdidas de eficiencia, aunque las pruebas realizadas con la utilidad M4 de GNU, han sido bastante satisfactorias.

Expresiones regulares:

La aplicación hace uso de expresiones regulares a la hora de verificar la regla de configuración a emplear. El uso de comparaciones sencillas tiene como resultado una pérdida notable de funcionalidad y posibilidades. El empleo de expresiones regulares para comparar permite, entre otras:

La aplicación hace uso de expresiones regulares a la hora de verificar la regla de configuración a emplear.

Debido a este último punto se consigue una gran funcionalidad: El agrupamiento de sub-expresiones regulares permite la exigencia de que los datos pasados por el cliente respondan a un formato específico, y obtener la información que nos interesa para pasarla al driver que se encargará de realizar la consulta.

Pongamos un ejemplo: Supongamos que nos interesa que el cliente nos pase una información en la query_string, y esta información debe ajustarse al formato:

  CAMPO=valor
    

Además nos interesa forzar que la query_string se ajuste a este formato y no haya información extra.

Nosotros podremos conseguir este propósito poniendo el siguiente término en la regla de selección:

  QUERY_STRING: "^CAMPO=[^&]*$";
	  

Con la línea anterior conseguiremos que la regla sólo se seleccione si la QUERY_STRING es de esta forma. Pero aún más, si en vez de poner la línea anterior, rodeamos la parte correspondiente al valor de unos paréntesis, el programa nos permitirá obtener, ya separada, la parte correspondiente al valor (además, en este proceso, el programa transforma las secuencias de escape de la forma %xx, introducidas por el navegador) con el siguiente término:

  QUERY_STRING: "^CAMPO=([^&]*)$";
    

¿Cómo funciona?

Ha llegado el momento de describir cómo funciona el programa. El programa, al arrancar, recibe del servidor web un conjunto de variables de entorno que transmiten información al mismo. Este conjunto de variables de entorno es todo lo que tiene el programa a su disposición para obtener datos de: ¿Quién es el cliente? ¿Desde donde hace la petición? ¿Qué consulta está haciendo? ¿Qué tipo de información (MIME type) puede soportar el cliente? etc. El programa permite seleccionar la regla que empleará basándose en los términos que aparecen en la parte izquierda de la regla. Estos son de tres tipos (actualmente):

Con toda esta artillería, ya tenemos las bases para empezar a construir reglas. A continuación agruparemos todos los términos que nos permitan construir una regla válida y los encerraremos entre llaves, para formar lo que llamaremos el lado izquierdo de una regla.

El lado izquierdo de una regla y el lado derecho se delimitan de igual modo (entre llaves) y se separan entre sí por el símbolo `->'.

El lado derecho contiene términos con la misma estructura: un nombre de variable, el carácter `:', una cadena de caracteres y el carácter `;'. Los términos del lado derecho son asignaciones de valores a variables, que se realizan, empleando los servicios de M4:

El resto de variables, pueden emplearse en el fichero plantilla, o serán empleadas por los drivers correspondientes.

¿Cómo pasa la información del usuario al CGI?

Muy fácil. Los grupos creados con las expresiones regulares de los términos del lado izquierdo de la regla son definidos en variables con nombres específicos (los nombres de las variables son `term_<i>_match_<j>', donde <i> hace referencia al número de término en la regla y <j> hace referencia al número del grupo (contando por la izquierda) en la expresión regular). Así, suponiendo que la query_string pasada por el cliente es:

`NOMBRE=JOSE&APELL1=DE+LA+FUENTE&APELL2=LOPEZ'

y que tenemos los siguientes términos en la regla:

     QUERY_STRING: "NOMBRE=([^&]*)";
     QUERY_STRING: "APELL1=([^&]*)";
     QUERY_STRING: "APELL2=([^&]*)";
Esto dará como resultado:
     term_0_match_0  <- "NOMBRE=JOSE";
     term_0_match_1  <- "JOSE";
     term_1_match_0  <- "APELL1=DE LA FUENTE";  
           (obsérvese la transformación de caracteres + por ` ')
     term_1_match_1  <- "DE LA FUENTE";
     term_2_match_0  <- "APELL2=LOPEZ";
     term_2_match_1  <- "LOPEZ";
Drivers:

No haremos una descripción de cómo se emplean los drivers en este artículo, dejando al lector interesado la posibilidad de leer la documentación del manual de referencia que acompaña la distribución.

Sólo diremos que en estos momentos tan sólo hay un driver, para conexión a bases de datos tipo POSTGRESQL (el nombre del driver es este, precisamente) aunque el autor planea realizar uno para acceso a bases de datos LDAP.

Ejemplo:

Veamos un ejemplo completo para ilustrar el uso del programa:

Valga la información de la Tabla de consultas a la base de datos de `AVISOS A LOS NAVEGANTES' de slug.ctv.es. Es un ejemplo muy sencillo, que permite ver como emplear dos plantillas para ver registros individuales de una tabla, o un listado completo.

/etc/html2sql.cfg
{
  PATH_INFO: "^/avisos/?$"; # Selecciona por PATH_INFO
  [SERVER_ADMIN: ".*"];     # Obtiene la info de SERVER_ADMIN. Opcional
} -> {
  DRIVER:   "POSTGRESQL";
  PGTTY:    "/dev/console";  # Envía trazas a consola.
  PGDATABASE:     "postgres";
# Hacemos una consulta (siempre ponemos el oid, que lo
# empleará internamente el fichero con la plantilla para 
# enlazar con los registros individuales).
  PGQUERY:  "select oid,ct,titulo,texto,mt"
      " from avisos"
      " where (dt is NULL or dt > 'now')"
      " order by mt desc";
# Fichero que contiene la plantilla.
  M4FILE:    "/usr/local/etc/httpd/plantillas_m4/avisos.m4";
  WEBMASTER: "term_1_match_0";
  #TESTMODE: "TRUE";
}

# Esta regla de selección permite seleccionar un aviso en el que se ha
# elegido un OID (clave primaria) La información va incluida en la
# variable PATH_INFO del CGI.

{
  PATH_INFO: "^/avisos/([0-9]+)/?$";
  SERVER_ADMIN: ".*";
} -> {
  DRIVER:   "POSTGRESQL";
  PGTTY:    "/dev/console";  # igual que antes, trazas por consola.
  PGDATABASE:     "postgres";
  OID:      "term_0_match_1"; # damos un nombre al OID.

# Nuevamente, la selección es importante.  Incluimos el OID al
# comienzo de los campos, por si quisieramos poner un hiperenlace para
# borrar este registro.

  PGQUERY:  "select oid,ct,titulo,texto,mt,dt,autor"
            " from avisos"
            " where (dt is NULL or dt > 'now') and oid=OID";
# La plantilla es diferente.
  M4FILE:    "/usr/local/etc/httpd/plantillas_m4/avisos_oid.m4";
  WEBMASTER: "term_1_match_0";
  #TESTMODE: "TRUE";
}

/usr/local/etc/httpd/plantillas_m4/avisos.m4
define(<<<for>>>, <<<dnl
ifelse(eval((<<<$2>>>) <= (<<<$3>>>)), 1,
<<<define(<<<$1>>>,<<<$2>>>)$4<<<>>>dnl
for(<<<$1>>>,eval(<<<$2>>>+1),<<<$3>>>, <<<$4>>>)dnl
>>>)dnl
>>>)dnl
divert(0)dnl
Mime-Version: 1.0
Content-type: text/html

<HTML>
  <BODY BGCOLOR="#ffffff">
    <CENTER>
      <H1>AVISOS A LOS NAVEGANTES QUE PASAN POR SLUG</h1>
    </center>
    <B>Nota:</b> Esta secci&oacute;n ha sido creada para dar a conocer
    cualquier noticia de inter&eacute;s relacionada con
    <A HREF="http://slug.ctv.es/">SLUG</a>,
    <A HREF="http://LuCAS.ctv.es/">LuCAS</a>,
    <A HREF="http://www.HispaLinux.ctv.es/">HispaLinux</a>
    y en general, cualquier servicio prestado por <B>slug.ctv.es</b>.<p>
    <CENTER><HR WIDTH=100></center>
ifelse(PGRES_RESULTSTATUS, <<<PGRES_TUPLES_OK>>>,<<<dnl
ifelse(PGRES_NTUPLES, 0,<<<dnl

      <!-- la tabla est\xe1 vac\xeda -->
      No hay avisos.<p>

>>>,<<<dnl /* PGRES_NTUPLES != 0 )( */
      <CENTER>
        <!-- contenido de la tabla -->
        <TABLE>
          <TR>
            <TH></th>
            <TH ALIGN="LEFT">Fecha-Hora&nbsp;</th>
            <TH ALIGN="LEFT">Asunto&nbsp;</th>
          </tr>

for(<<<i>>>,0,eval(PGRES_NTUPLES-1),<<<dnl
          <TR>
            <TD>
              <A HREF="/cgi-bin/pg2cgi/avisos/cell(i,0)">
                <IMG SRC="/icons/burst.gif">
              </a>
            </td>
            <TD><B>cell(i,1)&nbsp;</b></td>
            <TD>cell(i,2)&nbsp;</td>
          </tr>
>>>)dnl /* for */

        </table>
      </center>

>>>)dnl /* PGRES_NTUPLES */

>>>,<<<dnl /* ifelse PGRES_RESULTSTATUS )(*/

      Error en el resultado: <B>PGRES_RESULTSTATUS</b><BR>
      Mensaje del servidor: PGRES_ERRORMSG<P>

>>>)dnl

      <CENTER><HR WIDTH=100></center>
      <FONT SIZE=-2>
        <A HREF="mailto:WEBMASTER?subject=TABLON DE ANUNCIOS"><CODE>WEBMASTER</code></a>
      </font>
  </body>
</html>


/usr/local/etc/httpd/plantillas_m4/avisos_oid.m4
divert(-1)
$Id: generic_list.m4,v 1.1 1998/07/06 17:13:33 luis Exp $
define(<<<cell>>>, <<<PGRES_CELL_$1_$2>>>)
define(<<<field>>>, <<<PGRES_FNAME_$1>>>)
define(<<<for>>>, <<<dnl
ifelse(eval((<<<$2>>>) <= (<<<$3>>>)), 1,
<<<define(<<<$1>>>,<<<$2>>>)$4<<<>>>dnl
for(<<<$1>>>,eval(<<<$2>>>+1),<<<$3>>>, <<<$4>>>)dnl
>>>)dnl
>>>)dnl
divert(0)dnl
Mime-Version: 1.0
Content-type: text/html

<HTML>
  <BODY BGCOLOR="#ffffff">
    <CENTER>
      <H1>AVISO OID</h1>
    </center>
    <CENTER><HR WIDTH=100></center>

ifelse(PGRES_RESULTSTATUS, <<<PGRES_TUPLES_OK>>>,<<<dnl
ifelse(PGRES_NTUPLES, 0,<<<dnl

      <!-- la tabla est\xe1 vac\xeda -->
      No existe el aviso OID, o ha caducado.<p>

>>>,<<<dnl /* PGRES_NTUPLES != 0 )( */
      <CENTER>
        <!-- contenido de la tabla -->
        <TABLE>
          <TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Fecha de entrada:</b></font> </td>
            <TD width="80%">cell(0,1)</td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Fecha &uacute;ltima modif:</b></font> </td>
            <TD>cell(0,4)</td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Fecha eliminaci&oacute;n:</b></font> </td>
            <TD WIDTH=*>cell(0,5)</td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Autor:</b></font> </td>
            <TD><font size=+1><a href="mailto:cell(0,6)?subject=[TABLON-SLUG] cell(0,2)">cell(0,6)</a></font></td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Asunto:</b></font> </td>
            <TD><font size=+1><B>cell(0,2)<B></font></td>
          </tr><TR>
            <TD COLSPAN=2 BGCOLOR="#c0ffff"><font color="#404040">cell(0,3)</font></td>
          </tr>
        </table>
      </center>

>>>)dnl /* PGRES_NTUPLES */

>>>,<<<dnl /* ifelse PGRES_RESULTSTATUS )(*/

      Error en el resultado: <B>PGRES_RESULTSTATUS</b><BR>
      Mensaje del servidor: PGRES_ERRORMSG<P>

>>>)dnl

      <CENTER><HR WIDTH=100></center>
      <FONT SIZE=-2>
        <A HREF="mailto:WEBMASTER?subject=TABLON DE ANUNCIOS"><CODE>WEBMASTER</code></a>
      </font>
  </body>
</html>

Los resultados se pueden ver, consultando, por ejemplo:
http://slug.ctv.es/cgi-bin/pg2cgi/avisos/
ó
http://slug.ctv.es/cgi-bin/pg2cgi/avisos/20384

Descarga (Download)

El programa puede hallarse en la siguiente dirección del web:

http://slug.ctv.es/~luis/utils/pg2cgi-0.1.tar.gz
http://slug.ctv.es/~luis/utils/pg2cgi-0.1.README
ftp://slug.ctv.es/pub/slug/luis/pg2cgi-0.1.tar.gz
ftp://slug.ctv.es/pub/slug/luis/pg2cgi-0.1.README

Texto original en Castellano


Páginas web mantenidas por Miguel Ángel Sepúlveda
© Luis Colorado 1998
LinuxFocus 1998