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