Postgres Programmer
Postgres Programmer
Postgres Programmer
Editado por
Thomas Lockhart
Guia del Programador de PostgreSQL por El equipo de desarrollo de PostgreSQL Editado por Thomas Lockhart
Tabla de contenidos
Sumario........................................................................................................................... i 1. Introduccin ...............................................................................................................1 1.1. Recursos............................................................................................................1 1.2. Terminologa .....................................................................................................3 1.3. Notacin............................................................................................................4 1.4. Y2K Statement (Informe sobre el efecto 2000)................................................5 1.5. Copyrights y Marcas Registradas .....................................................................6 2. Arquitectura ...............................................................................................................1 2.1. Conceptos de Arquitectura de Postgres ............................................................1 3. Extensor SQL: Preludio ............................................................................................3 3.1. Como hacer extensible el trabajo ......................................................................3 3.2. El Tipo de Sistema de Postgres.........................................................................4 3.3. Acerca de los Sistema de Catalogo de Postgres................................................4 4. Extendiendo SQL: Funciones ...................................................................................7 4.1. Funciones de Lenguaje de Consultas (SQL).....................................................7 4.1.1. Ejemplos ................................................................................................8 4.1.2. Funciones SQL sobre Tipos Base ..........................................................9 4.1.3. Funciones SQL sobre Tipos Compuestos ............................................10 4.2. Funciones de Lenguaje Procedural .................................................................13 4.3. Funciones Internas ..........................................................................................13 4.4. Funciones de Lenguaje Compilado (C) ..........................................................14 4.4.1. Funciones de Lenguaje C sobre Tipos Base ........................................15 4.4.2. Funciones del Lenguaje C sobre Tipos Compuestos ...........................20 4.4.3. Escribiendo cdigo...............................................................................22 4.5. Sobrecarga de funciones .................................................................................24 4.5.1. Conictos en el Espacio de Nombres ..................................................24 4.5.1.1. Pre-v6.6.....................................................................................24 5. Extendiendo SQL: Tipos .........................................................................................26 5.1. Tipos Denidos por el Usuario .......................................................................26 5.1.1. Funciones Necesarias para un Tipo Denido por el Usuario...............26
5.1.2. Objetos Grandes...................................................................................28 6. Extenediendo SQL: Operadores.............................................................................30 6.1. Informacin de optimizacin de operador ......................................................31 6.1.1. COMMUTATOR (conmutador)...........................................................32 6.1.2. NEGATOR(negador) ...........................................................................33 6.1.3. RESTRICT (Restringir) .......................................................................34 6.1.4. JOIN (unir)...........................................................................................35 6.1.5. HASHES(desmenusamiento)...............................................................36 6.1.6. SORT1 and SORT2 (orden1 y orden2)................................................37 7. Extensiones de SQL: Agregados.............................................................................40 8. El Sistema de reglas de Postgres .............................................................................43 8.1. Qu es un rbol de query?.............................................................................43 8.1.1. Las partes de un rbol de query ...........................................................44 8.2. Las vistas y el sistema de reglas. ....................................................................46 8.2.1. Implementacin de las vistas en Postgres............................................47 8.2.2. Cmo trabajan las reglas de SELECT .................................................47 8.2.3. Reglas de vistas en instrucciones diferentes a SELECT......................57 8.2.4. El poder de las vistas en Postgres ........................................................59 8.2.4.1. Benecios..................................................................................59 8.2.4.2. Puntos delicados a considerar ...................................................60 8.2.5. Efectos colaterales de la implementacin ............................................61 8.3. Reglas sobre INSERT, UPDATE y DELETE .................................................62 8.3.1. Diferencias con las reglas de las vistas. ...............................................62 8.3.2. Cmo trabajan estas reglas...................................................................62 8.3.2.1. Una primera regla paso a paso. .................................................64 8.3.3. Cooperacin con las vistas...................................................................70 8.4. Reglas y permisos ...........................................................................................79 8.5. Reglas frente triggers ......................................................................................81 9. Utilizacin de las Extensiones en los ndices .........................................................86 10. GiST Indices ...........................................................................................................98 11. Lenguajes Procedurales.......................................................................................101 11.1. Instalacin de lenguajes procedurales.........................................................101
11.2. PL/pgSQL ...................................................................................................103 11.2.1. Panormica.......................................................................................103 11.2.2. Descripcion ......................................................................................104 11.2.2.1. Estructura de PL/pgSQL .......................................................104 11.2.2.2. Comments .............................................................................105 11.2.2.3. Declaraciones........................................................................105 11.2.2.4. Tipos de datos .......................................................................107 11.2.2.5. Expressions ...........................................................................108 11.2.2.6. Sentencias .............................................................................110 11.2.2.7. Procedimientos desencadenados...........................................114 11.2.2.8. Excepciones ..........................................................................116 11.2.3. Ejemplos ..........................................................................................116 11.2.3.1. Algunas funciones sencillas en PL/pgSQL...........................117 11.2.3.2. Funciones PL/pgSQL para tipos compuestos .......................117 11.2.3.3. Procedimientos desencadenados en PL/pgSQL....................118 11.3. PL/Tcl .........................................................................................................119 11.3.1. Introduccin .....................................................................................119 11.3.2. Descripcin ......................................................................................120 11.3.2.1. Funciones de Postgres y nombres de procedimientos Tcl ....120 11.3.2.2. Deniendo funciones en PL/Tcl ...........................................120 11.3.2.3. Datos Globales en PL/Tcl .....................................................121 11.3.2.4. Procedimientos desencadenados en PL/Tcl ..........................122 11.3.2.5. Acceso a bases de datos desde PL/Tcl..................................124 12. Enlazando funciones de carga dinmica............................................................129 12.1. ULTRIX ......................................................................................................130 12.2. DEC OSF/1 .................................................................................................131 12.3. SunOS 4.x, Solaris 2.x y HP-UX................................................................132 13. Triggers (disparadores) .......................................................................................134 13.1. Creacin de Triggers...................................................................................134 13.2. Interaccin con el Trigger Manager............................................................136 13.3. Visibilidad de Cambios en Datos................................................................138 13.4. Ejemplos .....................................................................................................139 14. Server Programming Interface...........................................................................145
14.1. Interface Functions......................................................................................145 SPI_connect .................................................................................................146 SPI_nish.....................................................................................................147 SPI_exec.......................................................................................................149 SPI_prepare..................................................................................................152 SPI_saveplan................................................................................................154 SPI_execp.....................................................................................................155 14.2. Interface Support Functions........................................................................158 SPI_copytuple ..............................................................................................158 SPI_modifytuple ..........................................................................................159 SPI_fnumber ................................................................................................161 SPI_fname....................................................................................................162 SPI_getvalue ................................................................................................164 SPI_getbinval ...............................................................................................166 SPI_gettype ..................................................................................................167 SPI_gettypeid...............................................................................................169 SPI_getrelname ............................................................................................171 SPI_palloc ....................................................................................................172 SPI_repalloc.................................................................................................173 SPI_pfree......................................................................................................174 14.3. Memory Management.................................................................................176 14.4. Visibility of Data Changes ..........................................................................177 14.5. Examples.....................................................................................................177 15. Objetos Grandes...................................................................................................182 15.1. Nota Histrica .............................................................................................182 15.2. Caractersticas de la Implementacin .........................................................182 15.3. Interfaces.....................................................................................................182 15.3.1. Creando un Objeto Grande ..............................................................183 15.3.2. Importando un Objeto Grande .........................................................184 15.3.3. Exportando un Objeto Grande .........................................................184 15.3.4. Abriendo un Objeto Grande Existente.............................................185 15.3.5. Escribiendo Datos en un Objeto Grande..........................................185 15.3.6. Leyendo Datos desde un Objeto Grande .........................................185
15.3.7. Posicionndose en un Objeto Grande ..............................................186 15.3.8. Cerrando un Descriptor de Objeto Grande ......................................186 15.4. Funciones registradas Incorporadas ............................................................186 15.5. Accediendo a Objetos Grandes desde LIBPQ ............................................187 15.6. Programa de Ejemplo..................................................................................187 16. libpq.......................................................................................................................195 16.1. Funciones de Conexin a la Base de Datos ................................................195 16.2. Funciones de Ejecucin de Consultas.........................................................207 16.3. Procesamiento Asncrono de Consultas......................................................213 16.4. Ruta Rpida.................................................................................................218 16.5. Noticacin Asncrona ...............................................................................220 16.6. Funciones Asociadas con el Comando COPY............................................221 16.7. Funciones de Trazado de libpq ...................................................................225 16.8. Funciones de control de libpq .....................................................................225 16.9. Variables de Entorno ...................................................................................226 16.10. Programas de Ejemplo ..............................................................................228 16.10.1. Programa de Ejemplo 1..................................................................228 16.10.2. Programa de Ejemplo 2..................................................................232 16.10.3. Programa de Ejemplo 3..................................................................237 17. libpq C++ Binding................................................................................................244 17.1. Control e Inicializacin...............................................................................244 17.1.1. Variables de Entorno. .......................................................................244 17.2. Clases de libpq++........................................................................................246 17.2.1. Clase de Conexin: PgConnection ...............................................246 17.2.2. Clase Base de Datos: PgDatabase .................................................246 17.3. Funciones de Conexin a la Base de Datos ................................................247 17.4. Funciones de Ejecucin de las Consultas ...................................................248 17.5. Noticacin Asncrona ...............................................................................253 17.6. Funciones Asociadas con el Comando COPY. ...........................................254 18. pgtcl .......................................................................................................................257 18.1. Comandos ...................................................................................................257 18.2. Ejemplos .....................................................................................................258 18.3. Informacin de referencia de comandos pgtcl ............................................259
pg_connect ...................................................................................................259 pg_disconnect ..............................................................................................261 pg_conndefaults ...........................................................................................262 pg_exec ........................................................................................................263 pg_result.......................................................................................................265 pg_select ......................................................................................................267 pg_listen.......................................................................................................269 pg_lo_creat...................................................................................................271 pg_lo_open...................................................................................................273 pg_lo_close ..................................................................................................274 pg_lo_read....................................................................................................275 pg_lo_write ..................................................................................................277 pg_lo_lseek ..................................................................................................278 pg_lo_tell .....................................................................................................279 pg_lo_unlink ................................................................................................281 pg_lo_import................................................................................................282 pg_lo_export ................................................................................................283 I. CCVS API Functions .............................................................................................286 ..............................................................................................................................287 19. Interfaz ODBC .....................................................................................................288 19.1. Trasfondo ....................................................................................................288 19.2. Aplicaciones Windows................................................................................289 19.2.1. Escritura de Aplicaciones ................................................................289 19.3. Instalacin Unix ..........................................................................................290 19.3.1. Construyendo el Driver....................................................................290 19.4. Ficheros de Conguracin ..........................................................................295 19.5. ApplixWare .................................................................................................297 19.5.1. Conguration ...................................................................................297 19.5.2. Problemas Comunes.........................................................................299 19.5.3. Depurando las conexiones ODBC ApplixWare...............................300 19.5.4. Ejecutando la demo ApplixWare .....................................................301 19.5.5. Useful Macros..................................................................................302 19.5.6. Plataformas soportadas ....................................................................303
20. JDBC Interface.....................................................................................................304 20.1. Building the JDBC Interface.......................................................................304 20.1.1. Compiling the Driver .......................................................................304 20.1.2. Installing the Driver .........................................................................305 20.1.2.1. Example ................................................................................305 20.2. Preparing the Database for JDBC ...............................................................305 20.3. Using the Driver..........................................................................................306 20.4. Importing JDBC..........................................................................................306 20.5. Loading the Driver ......................................................................................306 20.6. Connecting to the Database ........................................................................307 20.7. Issuing a Query and Processing the Result .................................................308 20.7.1. Using the Statement Interface..........................................................309 20.7.2. Using the ResultSet Interface...........................................................309 20.8. Performing Updates ....................................................................................310 20.9. Closing the Connection...............................................................................310 20.10. Using Large Objects .................................................................................311 20.11. Postgres Extensions to the JDBC API ......................................................313 20.12. Further Reading ........................................................................................369 21. Interfaz de Programacin Lisp...........................................................................371 22. Codigo Fuente Postgres .......................................................................................373 22.1. Formateo .....................................................................................................373 23. Revisin de las caractersticas internas de PostgreSQL...................................375 23.1. El camino de una consulta ..........................................................................375 23.2. Cmo se establecen las conexiones ............................................................376 23.3. La etapa de traduccin ................................................................................377 23.3.1. Traductor..........................................................................................377 23.3.2. Proceso de transformacin...............................................................380 23.4. El sistema de reglas de Postgres .................................................................381 23.4.1. El sistema de reescritura ..................................................................381 23.4.1.1. Tcnicas para implementar vistas .........................................381 23.5. Planicador/optimizador.............................................................................383 23.5.1. Generando planes posibles...............................................................383 23.5.2. Estructura de datos del plan .............................................................385
23.6. Ejecutor .......................................................................................................386 24. pg_options.............................................................................................................387 25. Optimizacin Gentica de Consulta en Sistemas de Base de Datos ................392 25.1. Planicador de consulta para un Problema Complejo de Optimizacin.....392 25.2. Algoritmo Genticos (AG)..........................................................................393 25.3. Optimizacin Gentica de Consultas (GEQO) en Postgres........................394 25.4. Futuras Tareas de Implementacin para el OGEC de Postgres ..................395 25.4.1. Mejoras Bsicas ...............................................................................396 25.4.1.1. Mejora en la liberacin de memoria cuando la consulta ya se ha procesado.................................................................................396 25.4.1.2. Mejora de las conguraciones de los parmetros del algoritmo gentico ........................................................................................396 25.4.1.3. Busqueda de una mejor solucin para el desbordamiento de entero............................................................................................396 25.4.1.4. Encotrar solucin para la falta de memoria ..........................397 Referencias...................................................................................................397 26. Protocolo Frontend/Backend ..............................................................................399 26.1. Introduccin ................................................................................................399 26.2. Protocolo .....................................................................................................400 26.2.1. Inicio ................................................................................................400 26.2.2. Consulta ...........................................................................................403 26.2.3. Llamada a funcin............................................................................405 26.2.4. Respuestas de noticacin ...............................................................406 26.2.5. Cancelacin de peticiones en progreso ............................................407 26.2.6. Finalizacin......................................................................................408 26.3. Tipos de Datos de Mensajes........................................................................408 26.4. Formatos de Mensajes.................................................................................409 27. Seales de Postgres...............................................................................................422 28. gcc Default Optimizations ...................................................................................424 29. Interfaces de Backend..........................................................................................426 29.1. Formato de chero BKI ..............................................................................426 29.2. Comandos Generales ..................................................................................427
10
29.3. Macro Commands.......................................................................................428 29.4. Comandos de Depuracin ...........................................................................429 29.5. Ejemplo .......................................................................................................430 30. Ficheros de pginas..............................................................................................431 30.1. Estructura de la pgina................................................................................431 30.2. Ficheros.......................................................................................................432 30.3. Bugs ............................................................................................................433 DG1. El Repositorio del CVS....................................................................................434 DG1.1. Organizacin del rbol de CVS...............................................................434 DG1.2. Tomando Las Fuentes Va CVS Annimo..............................................437 DG1.3. Tomando Los Fuentes Va CVSup..........................................................439 DG1.3.1. Preparando un Sistema Cliente CVSup........................................439 DG1.3.2. Ejecutando un Cliente CVSup .....................................................440 DG1.3.3. Instalando CVSup ........................................................................443 DG1.3.4. Instalacin a partir de los Fuentes................................................445 DG2. Documentacin.................................................................................................448 DG2.1. Mapa de la documentacin......................................................................448 DG2.2. El proyecto de documentacin ................................................................449 DG2.3. Fuentes de la documentacin ..................................................................450 DG2.3.1. Estructura del documento.............................................................450 DG2.3.2. Estilos y convenciones .................................................................452 DG2.3.3. Herramientas de autor para SGML ..............................................452 DG2.3.3.1. emacs/psgml......................................................................452 DG2.4. Haciendo documentaciones.....................................................................454 DG2.5. Pginas man ............................................................................................455 DG2.6. Generacin de copias impresas para v6.5 ...............................................456 DG2.6.1. Texto de copia impresa.................................................................456 DG2.6.2. Copia impresa postscript ..............................................................457 DG2.7. Herramientas ...........................................................................................459 DG2.7.1. Instalacin de RPM Linux ...........................................................459 DG2.7.2. Instalacin en FreeBSD................................................................460 DG2.7.3. Instalacin en Debian...................................................................461 DG2.7.4. Instalacin manual de las herramientas........................................462
11
DG2.7.4.1. Requisitos previos .............................................................462 DG2.7.4.2. Instalacin de Jade ............................................................464 DG2.7.4.3. Instalacin del DTD de DocBook .....................................465 DG2.7.4.4. Instalacin de las hojas de estilo DSSSL de Norman Walsh 466 DG2.7.4.5. Instalacin de PSGML ......................................................467 DG2.7.4.6. Instalacin de JadeTeX .....................................................469 DG2.8. Otras herramientas ..................................................................................470 Bibliografa .................................................................................................................471
12
Lista de tablas
3-1. Sistema de Catalogos de Postgres ............................................................................5 4-1. Tipos de C equivalentes para los tipos internos de Postgres ..................................15 9-1. Esquema de un ndice.............................................................................................86 9-2. Estrategias B-tree....................................................................................................88 9-3. Esquema de pg_amproc ........................................................................................93 18-1. Comandos pgtcl ...............................................................................................257 27-1. Seales Postgres .................................................................................................422 30-1. Muestra de Dibujo de Pgina..............................................................................431 DG2-1. Documentacin de Postgres............................................................................448
Tabla de guras
2-1. Cmo se establece una conexin ..............................................................................1 3-1. El principal sistema de catalogo de Postgres............................................................5
Tabla de ejemplos
23-1. Una SELECT sencilla.........................................................................................378
Sumario
Postgres, desarrollada originalmente en el Departamento de Ciencias de la Computacin de la Universidad de California en Berkeley, fu pionera en muchos de los conceptos de bases de datos relacionales orientadas a objetos que ahora empiezan a estar disponiblesen algunas bases de datos comerciales. Ofrece suporte al lenguage SQL92/SQL3, integridad de transacciones, y extensibilidad de tipos de datos. PostgreSQL es un descendiente de dominio pblico y cdigo abierto del cdigo original de Berkeley.
Captulo 1. Introduccin
Este documento es el manual del programador para el gestor de bases de datos PostgreSQL (http://postgresql.org/), desarrollado inicialmente en la Universidad de California en Berkeley. PostgreSQL se basa en Postgres versin 4.2 (http://s2k-ftp.CS.Berkeley.EDU:8000/postgres/postgres.html). El proyecto Postgres, liderado por el Profesor Michael Stonebraker, ha sido nanciado por la Agencia de Proyectos de Investigacin de Defensa Avanzados (DARPA), la Ocina de Investigacin del Ejrcito (ARO), la Fundacin Nacional de Ciencia (NSF), y ESL, Inc. La primera parte de este manual explica el enfoque extensible de Postgres y describe como Postgres puede ser ampliado por los usuarios aadiendo sus propios tipos, operadores, agregados y funciones, tanto en lenguaje de programacin como en lenguaje de bases de datos. Despus de una discusin del sistema de reglas de Postgres, discutimos las interfaces de disparo (trigger) y SPI. El manual concluye con una descripcin detallada de la interfaz de programacin y de las libreras de apoyo para varios lenguajes. Asumimos que el lector tiene buenos conocimientos de Unix y de programacin en C.
1.1. Recursos
Este manual est organizado en diferentes partes: Tutorial Introduccion para nuevos usuarios. No cubre caractersticas avanzadas. Guia del Usuario Informacion general para el usuario, incluye comandos y tipos de datos. Gua del Programador Informacin avanzada para programadores de aplicaciones. Incluyendo tipos y
Captulo 1. Introduccin
extensin de funciones, libreria de interfaces y lo referido al diseo de aplicaciones. Guia del Administrador Informacin sobre instalacin y administracin. Lista de equipo soportado. Guia del Desarrollador Informacin para desarrolladores de Postgres. Este documento es para aquellas personas que estan contribuyendo al proyecto de Postgres; la informacin refererida al desarrollo de aplicaciones aparece en la Guia del Programador. Actualmente incluido en la Guia del Programador. Manual de Referencia Informacin detallada sobre los comandos. Actualmente includo en la Guia del Usuario. Ademas de ste manual, hay otros recursos que le servirn de ayuda para la instalacion y el uso de Postgres: man pages Las pginas de manual(man pages) contienen mas informacin sobre los comandos. FAQs(Preguntas Frequentes) La seccin de Preguntas Frequentes(FAQ) contiene respuestas a preguntas generales y otros asuntos que tienen que ver con la plataforma en que se desarrolle. LEAME(READMEs) Los archivos llamados LEAME(README) estan disponibles para algunas contribuciones. Web Site El sitio web de Postgres (postgresql.org) contiene informacin que algunas
Captulo 1. Introduccin
distribuciones no incluyen. Hay un catlogo llamado mhonarc que contiene el histrico de las listas de correo electrnico. Aqu podr encontrar bastante informacin. Listas de Correo La lista de correo pgsql-general (mailto:pgsql-general@postgresql.org) (archive (http://www.PostgreSQL.ORG/mhonarc/pgsql-general/)) es un buen lugar para contestar sus preguntas. Usted! Postgres es un producto de cdigo abierto . Como tal, depende de la comunidad de usuarios para su soporte. A medida que empieze a usar Postgres, empezar a depender de otros para que le ayuden, ya sea por medio de documentacin o en las listas de correo. Considere contribuir lo que aprenda. Si aprende o descubre algo que no est documentado, escrbalo y contribuya. Si aade nuevas caractersticas al cdigo, hgalas saber. Aun aquellos con poca o ninguna experiencia pueden proporcionar correcciones y cambios menores a la documentacin, lo que es una buena forma de empezar. El pgsql-docs (mailto:pgsql-docs@postgresql.org) (archivo (http://www.PostgreSQL.ORG/mhonarc/pgsql-docs/)) de la lista de correos es un buen lugar para comenzar sus pesquisas.
1.2. Terminologa
En la documentacin siguiente, sitio (o site) se puede interpretar como la mquina en la que est instalada Postgres. Dado que es posible instalar ms de un conjunto de bases de datos Postgres en una misma mquina, este trmino denota, de forma ms precisa, cualquier conjunto concreto de programas binarios y bases de datos de Postgres instalados.
Captulo 1. Introduccin
El superusuario de Postgres es el usuario llamado postgres que es dueo de los cheros de la bases de datos y binarios de Postgres. Como superusuario de la base de datos, no le es aplicable ninguno de los mecanismos de proteccin y puede acceder a cualquiera de los datos de forma arbitraria. Adems, al superusuario de Postgres se le permite ejecutar programas de soporte que generalmente no estn disponibles para todos los usuarios. Tenga en cuenta que el superusuario de Postgres no es el mismo que el superusuario de Unix (que es conocido como root). El superusuario debera tener un identicador de usuario (UID) distinto de cero por razones de seguridad. El administrador de la base de datos (database administrator) o DBA, es la persona responsable de instalar Postgres con mecanismos para hacer cumplir una poltica de seguridad para un site. El DBA puede aadir nuevos usuarios por el mtodo descrito ms adelante y mantener un conjunto de bases de datos plantilla para usar concreatedb. El postmaster es el proceso que acta como una puerta de control (clearing-house) para las peticiones al sistema Postgres. Las aplicaciones frontend se conectan al postmaster, que mantiene registros de los errores del sistema y de la comunicacin entre los procesos backend. El postmaster puede aceptar varios argumentos desde la lnea de rdenes para poner a punto su comportamiento. Sin embargo, el proporcionar argumentos es necesario slo si se intenta trabajar con varios sitios o con uno que no se ejecuta a la manera por defecto. El backend de Postgres (el programa ejecutable postgres real) lo puede ejecutar el superusuario directamente desde el intrprete de rdenes de usuario de Postgres (con el nombre de la base de datos como un argumento). Sin embargo, hacer esto elimina el buffer pool compartido y bloquea la tabla asociada con un postmaster/sitio, por ello esto no est recomendado en un sitio multiusuario.
1.3. Notacin
... o /usr/local/pgsql/ delante de un nombre de chero se usa para representar el camino (path) al directorio home del superusuario de Postgres. En la sinopsis, los corchetes ([ y ]) indican una expresin o palabra clave opcional. Cualquier cosa entre llaves ({ y }) y que contenga barras verticales (|) indica que
Captulo 1. Introduccin
debe elegir una de las opciones que separan las barras verticales. En los ejemplos, los parntesis (( y )) se usan para agrupar expresiones booleanas. | es el operador booleano OR. Los ejemplos mostrarn rdenes ejecutadas desde varias cuentas y programas. Las rdenes ejecutadas desde la cuenta del root estarn precedidas por >. Las rdenes ejecutadas desde la cuenta del superusuario de Postgres estarn precedidas por %, mientras que las rdenes ejecutadas desde la cuenta de un usuario sin privilegios estarn precedidas por $. Las rdenes de SQL estarn precedidas por => o no estarn precedidas por ningn prompt, dependiendo del contexto.
Nota: En el momento de escribir (Postgres v6.5) la notacin de las rdenes agging (o ojos) no es universalmente estable o congruente en todo el conjunto de la documentacin. Por favor, enve los problemas a la Lista de Correo de la Documentacin (o Documentation Mailing List) (mailto:docs@postgresql.org).
El Equipo de Desarrollo Global (o Global Development Team) de PostgreSQL proporciona el rbol de cdigo de software de Postgres como un servicio pblico, sin garanta y sin responsabilidad por su comportamiento o rendimiento. Sin embargo, en el momento de la escritura:
Captulo 1. Introduccin
El autor de este texto, voluntario en el equipo de soporte de Postgres desde Noviembre de 1996, no tiene constancia de ningn problema en el cdigo de Postgres relacionado con los cambios de fecha en torno al 1 de Enero de 2000 (Y2K). El autor de este informe no tiene constancia de la existencia de informes sobre el problema del efecto 2000 no cubiertos en las pruebas de regresin, o en otro campo de uso, sobre versiones de Postgres recientes o de la versin actual. Podramos haber esperado or algo sobre problemas si existiesen, dada la base que hay instalada y dada la participacin activa de los usuarios en las listas de correo de soporte. Por lo que el autor sabe, las suposiciones que Postgres hace sobre las fechas que se escriben usando dos nmeros para el ao estn documentadas en la Gua del Usuario (http://www.postgresql.org/docs/user/datatype.htm) en el captulo de los tipos de datos. Para aos escritos con dos nmeros, la transicin signicativa es 1970, no el ao 2000; ej. 70-01-01 se interpreta como 1970-01-01, mientras que 69-01-01 se interpreta como 2069-01-01. Los problemas relativos al efecto 2000 en el SO (sistema operativo) sobre el que est instalado Postgres relacionados con la obtencin de "la fecha actual" se pueden propagar y llegar a parecer problemas sobre el efecto 2000 producidos por Postgres.
Dirjase a The Gnu Project (http://www.gnu.org/software/year2000.html) y a The Perl Institute (http://language.perl.com/news/y2k.html) para leer una discusin ms profunda sobre el asunto del efecto 2000, particularmente en lo que tiene que ver con el open source o cdigo abierto, cdigo por el que no hay que pagar.
Captulo 1. Introduccin
Postgres95 tiene Copyright 1994-5 por los Regentes de la Universidad de California. Se autoriza el uso, copia, modicacin y distribuicin de este software y su documentacin para cualquier propsito, sin ningn pago, y sin un acuerdo por escrito, siempre que se mantengan el copyright del prrafo anterior, este prrafo y los dos prrafos siguientes en todas las copias. En ningn caso la Universidad de California se har responsable de daos, causados a cualquier persona o entidad, sean estos directos, indirectos, especiales, accidentales o consiguientes, incluyendo lucro cesante que resulten del uso de este software y su documentacin, incluso si la Universidad ha sido noticada de la posibilidad de tales daos. La Universidad de California rehusa especcamente ofrecer cualquier garantia, incluyendo, pero no limitada nicamente a, la garanta implcita de comerciabilidad y capacidad para cumplir un determinado propsito. El software que se distribuye aqu se entrega "tal y cual", y la Universidad de California no tiene ninguna obligacin de mantenimiento, apoyo, actualizacin, mejoramiento o modicacin. Unix es una marca registrada de X/Open, Ltd. Sun4, SPARC, SunOS y Solaris son marcas registradas de Sun Microsystems, Inc. DEC, DECstation, Alpha AXP y ULTRIX son marcas registradas de Digital Equipment Corp. PA-RISC y HP-UX son marcas registradas de Hewlett-Packard Co. OSF/1 es marca registrada de Open Software Foundation.
Captulo 2. Arquitectura
2.1. Conceptos de Arquitectura de Postgres
Antes de continuar, debera usted conocer la arquitectura bsica del sistema Postgres. El conocimiento de como interactan las partes de Postgres debera aclararse algo durante el siguiente captulo. En la jerga de las bases de datos, Postgres utiliza un simple modelo cliente/servidor de "proceso por usuario". Una sesin de Postgres consiste en los siguientes procesos Unix (programas) cooperando:
Un proceso demonio supervisor (postmaster), la aplicacin de interface del usuario (frontend en ingls) (por ejemplo, el programa psql), y los uno o ms procesos servidores de acceso a la base de datos (backend en ingls) (el proceso postgres mismo).
Un nico postmaster maneja una coleccin dada de bases de datos en nico host. Tal coleccin se denomina una instalacin o un site. Las aplicaciones de frontend que quieren acceder a una base de datos dada en una instalacin realizan llamadas a la librera. La librera enva el requerimiento del usuario a travs de la red al postmaster (Cmo se establece una conexin(a)), quien en su turno arranca un nuevo proceso servidor de backend (Cmo se establece una conexin(b)) Figura 2-1. Cmo se establece una conexin
Captulo 2. Arquitectura
y se conecta el proceso cliente al nuevo servidor (Cmo se establece una conexin(c)). > A partir de aqu, el proceso cliente y el servidor se comunican entre ellos sin intervencin del postmaster. En consecuencia, el proceso postmaster est siempre corriendo, esperando llamadas, mientras que los procesos cliente y servidor vienen y van. La librera libpq permite a un nico proceso cliente tener mltiples conexiones con procesos servidores. Sin embargo, la aplicacin cliente sigue siendo un proceso mono-hebra. Las conexiones con multihebrado cliente/servidor no estn actualmente soportadas en libpq. Una implicacin de esta arquitectura es que el postmaster y los servidores siempre corren en la misma mquina (el servidor de base de datos), mientras que el cliente puede correr en cualquier sitio. Debe usted tener esto en cuenta, ya que los cheros que pueden estar accesibles en una mquina cliente, pueden no estarlo (o estarlo slo con un nombre de chero diferente) en la mquina servidor. Debera tener tambin en cuenta que postmaster y los servidores postgres corren bajo el user-id del "superusuario" de Postgres. Ntese que el superusuario de Postgres no tiene porqu ser un usuario especial (es decir, un usuario llamado "postgres"), aunque en muchos sistemas est instalado as. Ms an, el superusuario de Postgres denitivamente no debe de ser el superusuario de Unix, "root"! En cualquier caso, todos los cheros relacionados con una base de datos deben encontrarse bajo este superusuario de Postgres.
compilado .o o bibliotecas de intercambio) con lo que se implementa un nuevo tipo o funciones y Postgres cargara lo que requiera. El codigo escrito en SQL es mas dicil de aadir al servidor. Esta habilitad para modicar la operacion al vuelo hace de Postgres la unica suite para prototipos rapidos de nuevas aplicaciones y estructuras de almacenamiento.
un nombre que empieza por pg_. Las siguientes clases contienen informacion que debe de ser util para los usuarios nales. (Hay muchas otros sistemas de catalogo, pero estos raramente son pedidos directamente.) Tabla 3-1. Sistema de Catalogos de Postgres Nombre del Catalogo pg_database pg_class pg_attribute pg_index pg_proc pg_type pg_operator pg_aggregate pg_am pg_amop pg_amproc pg_opclass Descriccion base de datos clases atributos de clases indices secundarios procedimientos (ambos C y SQL) tipos (ambos base y complejo) operadores conjunto y conjunto de funciones metodo de acceso operador de metodo de acceso soporte de operador de metodo de acceso operador de clases de metodo de acceso
El manual de referencia da mas detalle de explicacion de estos catalogos y sus atributos. De cualquier manera, El principal sistema de catalogo de Postgres muestra su mayor entidades y sus relacionamiento en el sistema de catalogo. (Los atributos que
no se reeren a otras entidades no son mostrados si no son parte primaria de la llave. Este diagrama puede ser mas o menos incomprensible hasta que realmente comience a mirar los contenidos de los catalogos y vea como se relacionan entre si. Por ahora, lo principal seguir este diagrama:
En varias de las secciones que vienen a continuacion, presentaremos varias consultas compuestas en los catalogos del sistema que presentan informacion que necesitamos para extender el sistema. Mirado este diagrama podremos hacer que algunas de estas consultas compuestas (que a menudo se componen de tres o cuatro partes) sean Ms comprensibles mas faciles de entender, dado que sera capaz de ver los atributos usados en las claves importadas de los formularios de consulta de otras clases. Muchas caracteristicas distintas (clases, atributos, funciones, tipos, metodos de acceso, etc) estan estrechamente relacionadas en este esquema. Un simple comando de creacion puede modicar muchos de estos catalogos. Los tipos y procedimientos son elementos fundamentales de este esquema.
Nota: Usamos las palabras procedure y function de forma mas o menos indistinta.
Practicamente todo catalogo contiene alguna referencia a instancias en una o ambas clases. Por ejemplo, Postgres frecuentemente usa rmas de tipo (por ejemplo, de funciones y operadores) para identicar instancias unicas en otros catalogos.
Hay muchos otros atributos y relaciones que tienen signicados obvios, pero hay otros muchos (particularmente aquellos que tienen que ver con los metodos de acceso) que no los tienen. Las relaciones entre pg_am, pg_amop, pg_amproc, pg_operator y pg_opclass son particularmente diciles de complrender, y seran descritas en profundidad (en la seccion sobre tipos de interfase y operadores de indice) antes de que estudiemos las extensiones basicas.
funciones de lenguaje de consultas (funciones escritas en SQL) funciones de lenguaje procedural (funciones escritas en, por ejemplo, PLTCL o PLSQL) funciones de lenguaje de programacin (funciones escritas en un lenguaje de programacin compilado tales como C)
Cada clase de funcin puede tomar un tipo base, un tipo compuesto o alguna combinacin como argumentos (parmetros). Adems, cada clase de funcn puede devolver un tipo base o un tipo compuesto. Es ms fcil denir funciones SQL, as que empezaremos con ellas. Los ejemplos en esta seccin se puede encontrar tambin en funcs.sql y funcs.c.
simples. Notar que las comillas simples usadas en las consultas se deben escribir como smbolos de escape, precedindolas con dos barras invertidas. Los argumentos de la funcin SQL se pueden referenciar en las consultas usando una sintaxis $n: $1 se reere al primer argumento, $2 al segundo, y as sucesivamente. Si un argumento es complejo, entonces una notacin dot (por ejemplo "$1.emp") se puede usar para acceder a las propiedades o atributos del argumento o para llamar a funciones.
4.1.1. Ejemplos
Para ilustrar una funcin SQL sencilla, considere lo siguiente, que se podra usar para cargar en una cuenta bancaria:
create function TP1 (int4, float8) returns int4 as update BANK set balance = BANK.balance - $2 where BANK.acctountno = $1 select(x = 1) language sql;
Un usuario podra ejecutar esta funcin para cargar $100.00 en la cuenta 17 de la siguiente forma:
select (x = TP1( 17,100.0));
El ms interesante ejemplo siguiente toma una argumento sencillo de tipo EMP, y devuelve resultados mltiples:
select function hobbies (EMP) returns set of HOBBIES as select (HOBBIES.all) from HOBBIES where $1.name = HOBBIES.person language sql;
Notar que denimos una lista objetivo para la funcin (con el nombre RESULT), pero la lista objetivo de la consulta que llam a la funcin sobreescribi la lista objetivo de la funcin. Por esto, el resultado se etiqueta answer en vez de one. Es casi tan fcil denir funciones SQL que tomen tipos base como argumentos. En el ejemplo de abajo, note cmo nos referimos a los argumentos dentro de la funcin como $1 y $2:
CREATE FUNCTION add_em(int4, int4) RETURNS int4 AS SELECT $1 + $2; LANGUAGE sql; SELECT add_em(1, 2) AS answer; +-----+ |answer | +-----+
|3 | +-----+
Note el uso de la sintaxis $1.salary. Antes de adentrarnos en el tema de las funciones que devuelven tipos compuestos, debemos presentar primero la notacin de la funcin para proyectar atributos. La forma sencilla de explicar esto es que podemos normalmente usar la notacin atributo(clase) y clase.atributo indistintamente:
-
10
- esto es lo mismo que: - SELECT EMP.name AS youngster FROM EMP WHERE EMP.age < 30 SELECT name(EMP) AS youngster FROM EMP WHERE age(EMP) < 30; +-------+ |youngster | +-------+ |Sam | +-------+
Como veremos, sin embargo, no siempre es este el caso. Esta notacin de funcin es importante cuando queremos usar una funcin que devuelva una nica instancia. Hacemos esto embebiendo la instancia completa dentro de la funcin, atributo por atributo. Esto es un ejemplo de una funcin que devuelve una nica instancia EMP:
CREATE FUNCTION new_emp() RETURNS EMP AS SELECT \None\::text AS name, 1000 AS salary, 25 AS age, \(2,2)\::point AS cubicle LANGUAGE sql;
En este caso hemos especicado cada uno de los atributos con un valor constante, pero cualquier computacin o expresin se podra haber sustituido por estas constantes. Denir una funcin como esta puede ser delicado. Algunos de las deciencias ms importantes son los siguientes:
11
La orden de la lista objetivo debe ser exactamente la misma que aquella en la que los atributos aparezcan en la orden CREATE TABLE (o cuando ejecute una consulta .*). Se debe encasillar las expresiones (usando ::) muy cuidadosamente o ver el siguiente error:
WARN::function declared to return type EMP does not retrieve (EMP.*)
Al llamar a una funcin que devuelva una instancia, no podemos obtener la instancia completa. Debemos o bien proyectar un atributo fuera de la instancia o bien pasar la instancia completa a otra funcin.
SELECT name(new_emp()) AS nobody; +-----+ |nobody | +-----+ |None | +-----+
La razn por la que, en general, debemos usar la sintaxis de funcin para proyectar los atributos de los valores de retorno de la funcin es que el parser no comprende la otra sintaxis (dot) para la proyeccin cuando se combina con llamadas a funciones.
SELECT new_emp().name AS nobody; WARN:parser: syntax error at or near "."
Cualquier coleccin de ordenes en el lenguaje de consulta SQL se pueden empaquetar juntas y se pueden denir como una funcin. Las ordenes pueden incluir updates (es decir, consultas INSERT, UPDATE, y DELETE) as como SELECT. Sin embargo, la orden nal debe ser un SELECT que devuelva lo que se especique como el tipo de retorno de la funcin.
CREATE FUNCTION clean_EMP () RETURNS int4 AS DELETE FROM EMP WHERE EMP.salary <= 0; SELECT 1 AS ignore_this
12
13
que el nombre en SQL.) Normalmente, todas las funciones internas presentes en el backend se declaran como funciones SQL durante la inicializacin de la base de datos, pero un usuario podra usar CREATE FUNCTION para crear nombres de alias adicionales para una funcin interna.
La cadena que especica el chero objeto (la cadena en la clusula AS) debera ser el camino completo del chero de cdigo objeto para la funcin, unido por comillas simples. Si un smbolo de enlace se usa en la clusula AS, el smbolo de enlace se debera unir por comillas simples tambin, y debera ser exactamente el mismo que el
14
nombre de la funcin en el cdigo fuente C. En sistemas Unix la orden nm imprimir todos los smbolos de enlace de un objeto que se puede cargar de forma dinmica. (Postgres no compilar una funcin automticamente; se debe compilar antes de que se use en una orden CREATE FUNCTION. Ver abajo para informacin adicional.)
15
Built-In Type name oid oidvector path point regproc reltime text tid timespan tinterval uint2 uint4 xid
C Type (Name) oid (oidvector *) (PATH *) (POINT *) regproc or REGPROC RelativeTime (text *) ItemPointer (TimeSpan *) TimeInterval uint16 uint32 (XID *)
Dened In include/postgres.h include/postgres.h include/postgres.h utils/geo-decls.h utils/geo-decls.h include/postgres.h utils/nabstime.h include/postgres.h storage/itemptr.h include/c.h or include/postgres.h utils/nabstime.h include/c.h include/c.h include/postgres.h
Internamente, Postgres considera un tipo base como un "blob de memoria". Las funciones denidas por el usuario que usted dene sobre un tipo en turn denen la forma en que Postgres puede operar sobre l. Esto es, Postgres solo almacenar y recuperar los datos desde disco y solo usar sus funciones denidas por el usuario para introducir y procesar los datos, as como para obtener la salida de los datos. Los tipos base pueden tener uno de los tres formatos internos siguientes:
16
Los tipos por valor solo pueden tener 1, 2 o 4 bytes de longitud (incluso si su computadora soporta tipos por valor de otros tamaos). Postgres mismo solo pasa los tipos entero por valor. Debera tener cuidado al denir sus tipos para que tengan el mismo tamao (en bytes) en todas las arquitecturas. Por ejemplo, el tipo long es peligroso porque es de 4 bytes en algunas mquinas y de 8 bytes en otras, mientras que el tipo int es de 4 bytes en la mayora de las mquinas Unix (aunque no en la mayora de computadores personales). Una implementacin razonable del tipo int4 en las mquinas Unix podra ser:
/* 4-byte integer, passed by value */ typedef int int4;
En el otro lado, los tipos de longitud ja de cualquier tamao se pueden pasar por referencia. Por ejemplo, aqu se presenta una implementacin de ejemplo de un tipo de Postgres:
/* 16-byte structure, passed by reference */ typedef struct { double x, y; } Point;
Solo los punteros a tales tipos se pueden usar a la hora de pasarlos como argumentos de entrada o de retorno en las funciones de Postgres. Finalmente, todos los tipos de longitud variable se deben pasar tambin por referencia. Todos los tipos de longitud variable deben comenzar con un campo length de exactamente 4 bytes, y todos los datos que se tengan que almacenar dentro de ese tipo deben estar situados en la memoria inmediatamente a continuacin de ese campo length. El campo length es la longitud total de la estructura (es decir, incluye el tamao del campo length mismo). Podemos denir el tipo texto como sigue:
17
Obviamente, el campo data no es sucientemente largo para almacenar todas las cadenas posibles; es imposible declarar tal estructura en C. Al manipular tipos de longitud variable, debemos tener cuidado de reservar la cantidad de memoria correcta y de inicializar el campo length. Por ejemplo, si quisiramos almacenar 40 bytes en una estructura text, podramos usar un fragmento de cdigo como este:
#include "postgres.h" ... char buffer[40]; /* our source data */ ... text *destination = (text *) palloc(VARHDRSZ + 40); destination->length = VARHDRSZ + 40; memmove(destination->data, buffer, 40); ...
Ahora que hemos visto todas las estructuras posibles para los tipos base, podemos mostrar algunos ejemplos de funciones reales. Suponga que funcs.c es as:
#include <string.h> #include "postgres.h" /* By Value */ int add_one(int arg) { return(arg + 1);
18
} /* By Reference, Fixed Length */ Point * makepoint(Point *pointx, Point *pointy ) { Point *new_point = (Point *) palloc(sizeof(Point)); new_point->x = pointx->x; new_point->y = pointy->y; return new_point; } /* By Reference, Variable Length */ text * copytext(text *t) { /* * VARSIZE is the total size of the struct in bytes. */ text *new_t = (text *) palloc(VARSIZE(t)); memset(new_t, 0, VARSIZE(t)); VARSIZE(new_t) = VARSIZE(t); /* * VARDATA is a pointer to the data region of the struct. */ memcpy((void *) VARDATA(new_t), /* destination */ (void *) VARDATA(t), /* source */ VARSIZE(t)-VARHDRSZ); /* how many bytes */ return(new_t); } text * concat_text(text *arg1, text *arg2)
19
{ int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) VARHDRSZ; text *new_text = (text *) palloc(new_text_size); memset((void *) new_text, 0, new_text_size); VARSIZE(new_text) = new_text_size; strncpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1)VARHDRSZ); strncat(VARDATA(new_text), VARDATA(arg2), VARSIZE(arg2)VARHDRSZ); return (new_text); }
En otros sistemas, podramos tener que especicar la extensin del nombre del chero como .sl (para indicar que es una librera (o biblioteca) compartida).
20
/* for GetAttributeByNa-
21
fuera de la instancia actual. Tiene tres argumentos: el argumento de tipo TUPLE pasado a la funcin, el nombre del atributo deseado, y un parmetro de retorno que describe si el atributo es null. GetAttributeByName alinear los datos apropiadamente de forma que usted pueda convertir su valor de retorno al tipo deseado. Por ejemplo, si tiene un atributo name que es del tipo name, la llamada a GetAttributeByName sera as:
char *str; ... str = (char *) GetAttributeByName(t, "name", &isnull)
Aunque hay formas de construir nuevas instancias o de modicar las instancias existentes desde dentro de una funcin C, stas son demasiado complejas para discutirlas en este manual.
22
lenguajes, tales como FORTRAN y Pascal a menudo no siguen la misma convencin de llamada que C. Esto es, otros lenguajes no pasan argumentos y devuelven valores entre funciones de la misma forma. Por esta razn, asumiremos que las funciones de su lenguaje de programacin estn escritas en C. Las funciones C con tipos base como argumentos se pueden escribir de una forma sencilla. Los equivalentes C de los tipos internos de Postgres son accesibles en un chero C si PGROOT /src/backend/utils/builtins.h se incluye como un chero de cabecera. Esto se puede conseguir escribiendo
#include <utils/builtins.h>
al principio del chero fuente C. Las reglas bsicas para construir funciones C son las siguientes:
La mayora de los cheros cabecera (include) para Postgres deberan estar ya instalados en PGROOT /include (ver Figura 2). Debera incluir siempre
-I$PGROOT/include
en sus lneas de llamada a cc. A veces, podra encontrar que necesita cheros cabecera que estn en el cdigo fuente del servidor mismo (es decir, necesita un chero que no hemos instalado en include). En esos casos puede necesitar aadir uno o ms de
-I$PGROOT/src/backend -I$PGROOT/src/backend/include -I$PGROOT/src/backend/port/<PORTNAME> -I$PGROOT/src/backend/obj
Al reservar memoria, use las rutinas de Postgres palloc y pfree en vez de las rutinas de la librera de C correspondientes malloc y free. La memoria reservada por palloc se liberar automticamente al nal de cada transaccin, previniendo fallos de memoria.
23
Siempre cntrese en los bytes de sus estructuras usando memset o bzero. Varias rutinas (tales como el mtodo de acceso hash, hash join y el algoritmo sort) computan funciones de los bits puros contenidos en su estructura. Incluso si usted inicializa todos los campos de su estructura, puede haber varios bytes de relleno de alineacin (agujeros en la estructura) que pueden contener valores incorrectos o basura. La mayora de los tipos internos de Postgres se declaran en postgres.h, por eso es una buena idea incluir siempre ese chero tambin. Incluyendo postgres.h incluir tambin elog.h y palloc.h por usted. Compilar y cargar su cdigo objeto para que se pueda cargar dinmicamente en Postgres siempre requiere ags (o banderas) especiales. Ver Enlazando funciones de carga dinmica para una explicacin detallada de cmo hacerlo para su sistema operativo concreto.
24
4.5.1.1. Pre-v6.6
Para funciones escritas en C, el nombre SQL declarado en CREATE FUNCTION debe ser exactamente el mismo que el nombre real de la funcin en el cdigo C (debido a esto debe ser un nombre de funcin de C legal). Hay una sutil consecuencia de este restriccin: mientras las rutinas de carga dinmicas en la mayora de los sistemas operativos estn mas que felices de permitirle cargar cualquier nmero de libreras compartidas que contienen nombres de funciones conictivos (con idnticos nombres), pueden, de hecho, chapucear la carga de formas interesantes. Por ejemplo, si usted dene una funcin dinmicamente cargada que resulta tener el mismo nombre que una funcin perteneciente a Postgres, el cargador DEC OSF/1 dinmico hace que Postgres llame a la funcin dentro de l mismo preferiblemente a dejar que Postgres llame a su funcin. Por esto, si quiere que su funcin se use en diferentes arquitecturas, recomendamos que no sobrecargue los nombres de las funciones C. Hay un truco ingenioso para resolver el problema que se acaba de describir. Dado que no hay problemas al sobrecargar funciones SQL, usted puede denir un conjunto de funciones C con nombres diferentes y entonces denir un conjunto de funciones SQL con idnticos nombres que tomen los tipos de argumentos apropiados y llamen a la funcin C correspondiente. Otra solucin es no usar la carga dinmica, sino enlazar sus funciones al backend stticamente y declararlas como funciones INTERNAL. Entonces, las funciones deben tener todas nombres C distintos pero se pueden declarar con los mismos nombres SQL (siempre que los tipos de sus argumentos dieran, por supuesto). Esta forma evita la sobrecarga de una funcin wrapper (o envolvente) SQL, con la desventaja de un mayor esfuerzo para preparar un ejecutable del backend a medida. (Esta opcin est disponible slo en la versin 6.5 y posteriores, dado que las versiones anteriores requeran funciones internas para tener el mismo nombre en SQL que en el cdigo C.)
25
y una cadena de la forma (x, y) como la representacin externa de la cadena. Estas funciones normalmente no son difciles de escribir, especialmente la funcin de salida. Sin embargo, hay varios puntos a recordar:
26
Al denir su representacin externa (cadena), recuerde que al nal debe escribir un parser completo y robusto para esa representacin como su funcin de entrada!
Complex * complex_in(char *str) { double x, y; Complex *result; if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2) { elog(WARN, "complex_in: error in parsing return NULL; } result = (Complex *)palloc(sizeof(Complex)); result->x = x; result->y = y; return (result); }
Debera intentar hacer las funciones de entrada y salida inversas la una a la otra. Si no lo hace, tendr problemas serios cuando necesite volcar sus datos en un chero y despus leerlos (por ejemplo, en la base de datos de otra persona en otra computadora). Este es un problema particularmente comn cuando hay nmeros en punto otante de por medio.
27
Para denir el tipo complejo, necesitamos crear las dos funciones denidas por el usuario complex_in y complex_out antes de crear el tipo:
CREATE FUNCTION complex_in(opaque) RETURNS complex AS PGROOT/tutorial/obj/complex.so LANGUAGE c; CREATE FUNCTION complex_out(opaque) RETURNS opaque AS PGROOT/tutorial/obj/complex.so LANGUAGE c; CREATE TYPE complex ( internallength = 16, input = complex_in, output = complex_out );
Como se discuti antes, Postgres soporta totalmente vectores (o arrays) de tipos base. Adems, Postgres soporta vectores de tipos denidos por el usuario tambin. Cuando usted dene un tipo, Postgres automticamente proporciona soporte para vectores de ese tipo. Por razones histricas, el tipo vector tiene el mismo nombre que el tipo denido por el usuario con el carcter subrayado _ antepuesto. Los tipos compuestos no necesitan ninguna funcin denida sobre ellos, dado que el sistema ya comprende cmo son por dentro.
28
considerablemente menor que 8192 bytes, dado que las pginas y tuplas de sobrecarga de Postgres deben caber en esta limitacin de 8KB tambin. El valor real que cabe depende de la arquitectura de la mquina.
Si usted necesita un tipo ms grande para algo como un sistema de recuperacin de documentos o para almacenar bitmaps, necesitar usar la interfaz de grandes objetos de Postgres.
29
30
Nosotros hemos mostrado como crear un operador binario aqu. Para crear un operador unario, solamente omite un argumento izquierdo (para unario izquierdo) o un argumento derecho (para unario derecho). El procedimietno y las sentencias del argumneto son los nicos items requeridos para CREATE OPERATOR (Crear operador). La sentencia COMMUTATOR (conmutador) mostrada en esta ejemplo es una indicacin opcional para optimizar la consulta. Adems de los detalles acerca de COMMUTATOR, aparecen otras indicaciones de optimizacin.
Una denicin de un operador Postgres puede incluir muchas sentencias opcionales que dicen al sistema cosas tiles acerca de como el operador se comporta. Estas sentencias deben ser proveidas siempre que sea apropiado, porque ellas pueden hacer considerablemente una ms rpida ejecucin de la consulta que usa el operador. Pero si tu las provees, debes estar seguro que estn bien! Un incorrecto uso de una sentencia de optimizacin puede resultar en choques nales, salidas sutilmente errneas, o otras
31
cosas malas. Tu puedes no poner las sentencias de optimizacin si no ests seguro de ellas, la nica consecuencia es que las consultas pueden ejecutarse ms lentamente de lo necesario. Sentencias de optimizacin adicionales pueden ser agregadas en versiones posteriores de Postgres. Las descrptas aqu son todas aquellas que la versin 6.5 entinede.
Un mtodo es omitir la sentencia COMMUTATOR en el primer operador que tu denes, y entonces proveer uno en la segunda denicin de operador. Desde que Postgres sabe que operadores conmutativos vienen de a pares, cuando ve la segunda denicin automaticamente volver y llenar en la sentencia COMMUTATOR faltante en la primera denicin. La otra, una manera mas honesta es solamente incluir la sentencia COMMUTATOR en ambas deniciones. Cuando Postgresprocesa la primera denicin y se da cuenta
32
COMMUTATOR hace referencia a un opereador inexistenete, el sistema har una entrada silenciosa para ese operador en la tabla (relacin) pg_operator del sistema. Esta entrada silenciosa tendr datos vlidos solamente para el nombre del operador, tipos de argumentos izquierdo y derechos, y un tipo de resultado, debido qeu es todo lo que Postgrespuede deducir en ese punto. La primera entrada la catlogo del operador enlazar hacia esa entrada silenciosa. Mas tarde, cuando tu denas el segundo operador, el sistema actualiza la entrada silenciosa con la informacin adicional de la segunda denicin. Si tu tratas de usar el operador silenciosa antes de que sea llenado, tu solo obtendras un mensaje de error. (Nota: Este procedimiento no funciona correctamente en versiones de Postgres anteriores a la 6.5, pero es ahora la manera recomendada de hacer las cosas.)
6.1.2. NEGATOR(negador)
La sentencia NEGATOR, si es proveida, nombra a un operador que es el negador del operador que est siendo denido. Nosotros decimos que un operador A es el negdor de un operador B si ambos retornan resultados bouleanos y (x A y) no es igual (x B y) para todas las posibles entradas x,y. Nota que B es tambin el negador de A. Por ejemplo, < and >= son un par de negadores para la mayora de los tipos de datos. A diferencia de CONMMUTATOR, un par de operadores unarios, pueden ser validamente marcados como negadores entre si; eso signicara (A x) no es igual (B x) para todo x, o el equivalente para operadores unarios derechos. Un operador de negacin debe tener el mismo tipo de argumento derecho y/o izquierdo como el operador en si mismo, entonces al igual que con COMMUTATOR, solo el nombre del operador necesita ser dado en la sentencia NEGATOR. Proveer NEGATOR es de mucha ayuda para la optimizacin de las consultas desde que permite expresiones como NOT (x=y) ser simplicadas en x <> y. Esto aparece mas seguido de los que tu debes pensar, porque NOTs pueden ser insertados como una consecuencia de otras reconstrucciones.
33
Pares de operadores negadores pueden ser denidos usando el mismo mtodo explicado para pares de conmutadores.
para el operador corriente y un valor constante particualr. Esto asiste al optimizador dandole alguna idea de como tantas las van a ser eliminadas por la sentencia WHERE que tiene este formulario. (Qu pasa si la constante est a la izquierda, debes puedes estar preguntndote? bien, esa es una de las cosas para las que sirve CONMMUTATOR...) Escribiendo nuevas funciones de estimacin selectivas de restriccin est ms all del alcance de este captulo, pero afortunadamente tu puedes usualmente solamente usar un estimador estandar del sistema para muchos de tus propios operadores. Estos son los estimadores estandars:
eqsel for = neqsel for <> scalarltsel for < or <= scalargtsel for > or >=
Puede parecer un poco curioso que estas son las categorias, pero ellas tienen sentido si tu piensas acerca de ellas. = tipicamente aceptar solamente una fraccin pequea de las las en la tabla; <> tipicamente rechazar solamente una fraccin pequea. <
34
aceptara una fraccin que depende de donde las constantes dadas quedan en el rango de valores para es columna de la tabla (la cual, solo ha pasado, es informacin recogida por el ANALIZADOR VACUUM y puesta disponible para el estimador selectivo). <= aceptar una fraccin un poquito mas laraga que < para las mismas constantes comparadas, pero son lo sucientemente cerradas para no ser costosas, especialmente desde que nosotros no estamos probalemente haciendo mejor que una aspera suposicin de cualquier manera. Los mismos comentarios son aplicados a > y >=. Tu puedes frecuentemente escaparse de usar eqsel o neqsel para operadores que tienen muy alta o muy baja selectividad, incluso si ellas no son realmente equivalentes o no equivalentes. Por ejemplok, la expresin regular operadores emparejados (~,~*, etc.) usa eqsel sobre la suposicin que ellos usualmente solo emparejen una pequea fraccinde entradas en una tabla. Tu puedes usar scalarltsel y scalargtsel para comparaciones sobre tipos de datos que tienen cierto signicado sencible de ser convertido en escalares numricos para comparaciones de rango. Es posible, aadir el tipo de dato a aquellos entendidos por la rutina convert_to_scalar() in src/backend/utils/adt/selfuncs.c. (Eventualmente, esta rutina debe ser reemplazada por funciones per-datatype identicadas a travz de una columna de la tabla pg_type; pero eso no ha pasado todava.)Si tu no haces esto, las cosas seguiran funcionando, pero la estimacin del optimizador no estarn tan bien como podrian. Hay funciones adicionales de selectividad diseadas para operadores geomtricos en src/backend/adt/geo_selfuncs.c: areasel, positionsel, y contsel. En este escrito estas son solo nombradas, pero tu puedes querer usarlas (o incluso mejormejorarlas).
35
para el operador corriente. Como la sentencia RESTRICT, esta ayuda al optimizador muy sustancialmente permitiendole deducir cual de las posibles secuencias join es probable que tome el menor trabajo. como antes, este captulo no procurar explicar como escribir una funcin estimadora selectiva join, pero solamente sugeriremos que tu uses uno de los estimadores estandars si alguna es aplicable:
eqjoinsel for = neqjoinsel for <> scalarltjoinsel for < or <= scalargtjoinsel for > or >= areajoinsel for 2D area-based comparisons positionjoinsel for 2D position-based comparisons contjoinsel for 2D containment-based comparisons
6.1.5. HASHES(desmenusamiento)
La sentencia HASHES, si est presente, le dice al sistema que est bien usar un metodo hash join para uno basado en join sobre este operador. HASHES solamente tiene sentido para operadores binarios que retornan valores binario, y en la prctica el operador tiene que estar igualado a algn tipo de datos. La suposicin subyacente de hash join es que el operador join puede selamente retornar TRUE (verdadero) para pares de valores izquierdos o derechos. Si dos valores puestos en diferentes recipientes hash, El join nunca los comparar a ellos del todo, implicitamente asumiendo que el resultado del operadior join debe ser FALSE (falso).
36
Entonces nunca tiene sentido especicar HASHES para operadores que no se representan igualmente. De hecho, la igualdad lgica no es sucientemente buena; el operador tuvo mejor representacin por igualdad pura bit a bit, porque la funcin hash sera computada sobre la representacin de la memoria de los valores sin tener en cuenta que signican los bits. Por ejemplo, igualdad de intervalos de tiempo no es igualdad bit a bit; el operador de igualdad de intervalo considera dos intervalos de tiempos iguales si ellos tienen la misma duracin, si son o no son sus puntos nales idnticos. Lo que esto signica es que el uso de join "=" entre campos de intervalos producira resultados diferentes si es implementado como un hash join o si es implementado en otro modo, porque una fraccin larga de los pares que deberan igualar resultarn en valores diferentes y nunca sern comparados por hash join. Pero si el optimizador elige usar una clase diferente de join, todoslos pares que el operador de igualdad dija que son iguales sern encontrados. No queremos ese tipo de inconsistencia, entonces no marcamos igualdad de intervalos como habilitados para hash. Hay tambin modos de dependencia de mquina en cuales un hash join puede fallar en hacer las cosas bien. Por ejemplo, si tu tipo de dato es una estructura en la cual puede haber bloque de bits sin interes, es inseguro marcar el operador de iguladad HASHES. (al menos, quizas, tu escribes tu otro operador para asegurarte que los bits sin uso son iguales a zero). Otro ejemplo es que los tipo de datos de punto otante son inseguros para hash joins. Sobre mquinas que cumplan los estndares de la IEEE de puntos otantes, menos cero y mas cero son dos valores diferentes (diferentes patrones de bit) pero ellos estn denidos para compararlos igual. Entonces, si la igualdad de punto otante estuviese marcada, hashes, un mnos cero y un mas cero probablemente no seran igualados por hash join, pero ellos seran igualados por cualquier otro proceso join. La ltima linea es para la cual tu probablemente deberas usar unicamente HASEHES para igualdad de operadores que son (o podra ser ) implementada por memcmp().
37
mtodo merge join (unir join) para un join basado sobre el operador corriente. Ambos deben ser especicados si tampoco est. El operador corriente debe ser igual para algunos pares de tipo de datos, y las sentencias SORT1 Y SORT2 nombran el operador de orden (< operador) para tipos de datos izquierdo y derecho respectivamente. Merge join est basado sobre la dea de ordenar las tablas izquierdas y derechas en un orden y luego inspecionarlas en paralelo. Entonces, ambos tipos de datos deben ser aptos de ser ordenados completamente, y el operador join debe ser uno que pueda solamente tener xito con pares de valores que caigan en el mismo lugar en la busqueda de orden. En prctica esto signica que el operador join debe comportarse como iguldad. Pero distinto de hashjoin, cuando los tipos de datos izquierdos y derechos tuvieron qe ser mejor el mismo. ( o al menos iquivalentes bit a bit), es posible unir dos tipos de datos distintos tanto como sean ellos compatibles logicamente. Por ejemplo, el operador de igualdad int2-versus-int4 es unible. Solo necesitamos operadores de oprden que traigna ambos tipos de datos en una secuencia lgica compatible. Cuando se especican operadores operadores sort merge, el operador corriente y ambos operadores referenciados deben retornar valores bouleanos; el operador SORT1 debe tener ambos tipo de datos de entrada iguales al tipo de argumento izquierdo del operador corriente y el operador SORT2 debe tener ambos tipos de datos de entrada iguales al tipo de argumento derecho del operador corriente. (como con COMMUTATOR y NEGATOR, esto signica que el nombre del operador es suciente para especicar el operador, y el sistema es capaz de hacer entradas de operador silenciosas si tu deniste el operador de igualda antes que los otros. En prctica tu debes solo escribier sentencias SORT para un operador =, y los dos operadores referenciados deben ser siempre nombrados <. Tratando de usar merge join con operadores nombrados nada mas resultar en confusiones inesperadas, por razones que veremos en un momento. Hay restricciones adicionales sobre operadores que tu marcas mergegoinables. Estas restricciones no son corrientemente chequeadas por CREATE OPERATE, pero un merge join puede fallar en tiempo de ejecucin si alguna no es verdad:
El operador de igualdad mergejoinable debe tener un conmutador (El mismo si los dos tipos de datos son iguales, o un operador de igualdad relativo si son diferentes.).
38
Debe haber operadores de orden < and > teniendo los mismos tipos de datos izquierdo y derecho de entrada como el operados mergejinable en si mismo. Estos operadores debenser nombrados < and >; tu no tienes opcion en este problema, desde que no hay provicin para especicarlos explicitamente. Nota que si los tipo de datos izquierdo y derechos son diferentes, ninguno de estos operadors es el mismo que cualquier operador SORT. pero ellos tuvieron mejores ordenados la compativilidad de los valores de dato con los operadores SORT, o o mergejoin fallar al funcionar.
39
Si solamente denimos sfunc2, estamos especicando un agregado que computa una funcion que es independiente de los atributos de cada instancia. "Count" es el ejemplo ms comn de este tipo de agregado. . "Count" comienza a cero y aade uno a su total
40
para cada instancia, ignorando el valor de instancia. Aqu, utilizamos la rutina integrada int4inc para hacer el trabajo por nosotros. Esta rutina incrementa (aade uno) su argumento.
CREATE AGGREGATE my_count ( sfunc2 = int4inc, - add one basetype = int4, stype2 = int4, initcond2 = 0 ); SELECT my_count(*) as emp_count from EMP; +-------+ |emp_count | +-------+ |5 | +-------+
"Average" es un ejemplo de un agregado que requiere tanto una funcin para calcular la suma actual y una funcin para calcular el contador actual. Cuando todas las instancias han sido procesadas, la respuesta nal para el agregado es la suma actual dividida por el contador actual. Utilizamos las rutinas int4pl y int4inc que utilizamos anteriormente as como tambin la rutina de divisin entera de Postgres , int4div, para calcular la divisin de la suma por el contador.
CREATE AGGREGATE my_average ( sfunc1 = int4pl, - sum basetype = int4, stype1 = int4, sfunc2 = int4inc, - count stype2 = int4, finalfunc = int4div, - division initcond1 = 0, initcond2 = 0
41
); SELECT my_average(salary) as emp_average FROM EMP; +--------+ |emp_average | +--------+ |1640 | +--------+
42
43
Ahora, qu es un rbol de query? Es una representacin interna de una instruccin SQL donde se almacenan de modo separado las partes menores que la componen. Estos rboles de query son visibles cuando arrancamos el motor de Postgres con nivel de debug 4 y tecleamos queries en el interface de usuario interactivo. Las acciones de las reglas almacenadas en el catalgo de sistema pg_rewrite estn almacenadas tambin como rboles de queries. No estn formateadas como la salida del debug, pero contienen exactamente la misma informacin. Leer un rbol de query requiere experiencia y era bastante duro cuando empec a trabajar en el sistema de reglas. Puedo recordar que mientras estaba esperando en la mquina de caf asimilaba el vaso a una lista de objetivos, el agua y el polvo del caf a una tabla de rangos, y todos los botones a expresiones de cualicacin. Puesto que las representaciones de SQL de rboles de queries son sucientes para entender el sistema de reglas, este documento no le ensear como leerlo. l debera ayudarle a aprenderlo, con las convenciones de nombres requeridas en las descripciones que siguen ms adelante.
44
por el que se la identica en las otras partes de la query. En un rbol de query, las entradas de la tabla de rango se indican por un ndice en lugar de por su nombre como estaran en una instruccin SQL. Esto puede ocurrir cuando se han mezclado las tablas de rangos de reglas. Los ejemplos de este documento no muestran esa situacin.
La relacin-resultado (resultrelation). Un ndice a la tabla de rango que identica la relacin donde irn los resultados de la query. Las queries SELECT normalmente no tienen una relacin resultado. El caso especial de una SELECT INTO es principalmente identica a una secuencia CREATE TABLE, INSERT ... SELECT y no se discute aqu por separado. En las queries INSERT, UPDATE y DELETE, la relacin resultado es la tabla (o vista!) donde tendrn efecto los cambios.
La lista objetivo (targetlist). La lista objetivo es una lista de expresiones que denen el resultado de la query. En el caso de una SELECT, las expresiones son las que construyen la salida nal de la query. Son las expresiones entre las palabras clave SELECT y FROM (* es slo una abreviatura de todos los nombres de atributos de una relacin). Las queries DELETE no necesitan una lista objetivo porque no producen ningn resultado. De hecho, el optimizador aadir una entrada especial para una lista objetivo vaca. Pero esto ocurre tras el sistema de reglas y lo comentaremos ms tarde. Para el sistema de reglas, la lista objetivo est vaca. En queries INSERT la lista objetivo describe las nuevas las que irn a la relacin resultado. Las columnas que no aparecen en la relacin resultado sern aadidas por el optimizador con una expresin constante NULL. Son las expresiones de la clausula VALUES y las de la clausula SELECT en una INSERT .... SELECT.
45
En queries UPDATE, describe las nuevas las que reemplazarn a otras viejas. Ahora el optimizador aadir las columnas que no aparecen insertando expresiones que recuperan los valores de las las viejas en las nuevas. Y aadir una entrada especial como lo hace DELETE. Es la parte de la query que recoge las expresiones del atributo SET atributo = expresin. Cada entrada de la lista objetivo contiene una expresion que puede ser un valor constante, una variable apuntando a un atributo de una de las relaciones en la tabla de rango, un parmetro o un arbol de expresiones hecho de llamadas a funciones, constantes, variables, operadores, etc.
La cualicacin. La cualicacin de las queries es una expresin muy similar a otra de las contenidas en las entradas de la lista objetivo. El valor resultado de esta expresin e un booleano que dice si la operacin (INSERT, UPDATE, DELETE o SELECT) para las las del resultado nal deber ser ejecutada o no. Es la clausula WHERE de una instruccin SQL. the others Las otras partes de un arbol de query, como la clausula ORDER BY, no tienen inters aqu. El sistema de reglas sustituye las entradas aqu presentes mientras est aplicando las reglas, pero aquellas no tiene mucho que hacer con los fundamentos del sistema de reglas. GROUP BY es una forma especial en la que aparece una denicin de una vista, y an necesita ser documentado.
46
y la secuencia:
CREATE TABLE myview (la misma lista de atributos de mytab); CREATE RULE "_RETmyview" AS ON SELECT TO myview DO INSTEAD SELECT * FROM mytab;
Porque esto es exactamente lo que hace internamente el comando CREATE VIEW. Esto tiene algunos efectos colaterales. Uno de ellos es que la informacin sobre una vista en el sistema de catlogos de Postgres es exactamente el mismo que para una tabla. De este modo, para los traductores de queries, no hay diferencia entre una tabla y una vista, son lo mismo: relaciones. Esto es lo ms importante por ahora.
47
El ejemplo para este documento son dos vistas unidas que hacen algunos clculos y algunas otras vistas utilizadas para ello. Una de estas dos primeras vistas se personaliza ms tarde aadiendo reglas para operaciones de INSERT, UPDATE y DELETE de modo que el resultado nal ser una vista que se comporta como una tabla real con algunas funcionalidades mgicas. No es un ejemplo fcil para empezar, y quiz sea demasiado duro. Pero es mejor tener un ejemplo que cubra todos los puntos discutidos paso a paso que tener muchos ejemplos diferentes que tener que mezclar despus. La base de datos necesitada para ejecutar los ejemplos se llama al_bundy. Ver pronto el porqu de este nombre. Y necesita tener instalado el lenguaje procedural PL/pgSQL, ya que necesitaremos una pequea funcin min() que devuelva el menor de dos valores enteros. Creamos esta funcin como:
CREATE FUNCTION min(integer, integer) RETURNS integer AS BEGIN IF $1 < $2 THEN RETURN $1; END IF; RETURN $2; END; LANGUAGE plpgsql;
Las tablas reales que necesitaremos en las dos primeras descripciones del sistema de reglas son estas:
CREATE TABLE shoe_data ( shoename char(10), sh_avail integer, slcolor char(10), slminlen float, slmaxlen float, slunit char(8) ); CREATE TABLE shoelace_data ( sl_name char(10), datos de zapatos clave primaria (primary key) nmero de pares utilizables color de cordn preferido longitud mnima de cordn longitud mxima del cordn unidad de longitud
48
nmero de pares utilizables color del cordn longitud del cordn unidad de longitud
Pienso que la mayora de nosotros lleva zapatos, y puede entender que este es un ejemplo de datos realmente utilizables. Bien es cierto que hay zapatos en el mundo que no necesitan cordones, pero nos har ms facil la vida ignorarlos. Las vistas las crearemos como:
CREATE VIEW shoe AS SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, sh.slminlen * un.un_fact AS slminlen_cm, sh.slmaxlen, sh.slmaxlen * un.un_fact AS slmaxlen_cm, sh.slunit FROM shoe_data sh, unit un WHERE sh.slunit = un.un_name; CREATE VIEW shoelace AS SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm FROM shoelace_data s, unit u WHERE s.sl_unit = u.un_name;
49
CREATE VIEW shoe_ready AS SELECT rsh.shoename, rsh.sh_avail, rsl.sl_name, rsl.sl_avail, min(rsh.sh_avail, rsl.sl_avail) AS total_avail FROM shoe rsh, shoelace rsl WHERE rsl.sl_color = rsh.slcolor AND rsl.sl_len_cm >= rsh.slminlen_cm AND rsl.sl_len_cm <= rsh.slmaxlen_cm;
El comando CREATE VIEW para la vista shoelace (que es la ms simple que tenemos) crear una relacin shoelace y una entrada en pg_rewrite que dice que hay una regla de reescritura que debe ser aplicada siempre que la relacin shoelace sea referida en la tabla de rango de una query. La regla no tiene cualicacin de regla (discutidas en las reglas no SELECT, puesto que las reglas SELECT no pueden tenerlas) y es de tipo INSTEAD (en vez de). Ntese que la cualicacin de las reglas no son lo mismo que las cualicacin de las queries! La accin de las reglas tiene una cualicacin. La accin de las reglas es un rbol de query que es una copia exacta de la instruccin SELECT en el comando de creacin de la vista.
Nota:: Las dos tablas de rango extra para NEW y OLD (llamadas *NEW* y *CURRENT* por razones histricas en el rbol de query escrito) que se pueden ver en la entrada pg_rewrite no son de interes para las reglas de SELECT.
Ahora publicamos unit, shoe_data y shoelace_data y Al (el propietario de al_bundy) teclea su primera SELECT en esta vida.
al_bundy=> al_bundy=> al_bundy=> al_bundy=> al_bundy=> al_bundy-> INSERT INTO unit VALUES (cm, 1.0); INSERT INTO unit VALUES (m, 100.0); INSERT INTO unit VALUES (inch, 2.54); INSERT INTO shoe_data VALUES (sh1, 2, black, 70.0, 90.0, cm);
50
al_bundy=> INSERT INTO shoe_data VALUES al_bundy-> (sh2, 0, black, 30.0, 40.0, inch); al_bundy=> INSERT INTO shoe_data VALUES al_bundy-> (sh3, 4, brown, 50.0, 65.0, cm); al_bundy=> INSERT INTO shoe_data VALUES al_bundy-> (sh4, 3, brown, 40.0, 50.0, inch); al_bundy=> al_bundy=> INSERT INTO shoelace_data VALUES al_bundy-> (sl1, 5, black, 80.0, cm); al_bundy=> INSERT INTO shoelace_data VALUES al_bundy-> (sl2, 6, black, 100.0, cm); al_bundy=> INSERT INTO shoelace_data VALUES al_bundy-> (sl3, 0, black, 35.0 , inch); al_bundy=> INSERT INTO shoelace_data VALUES al_bundy-> (sl4, 8, black, 40.0 , inch); al_bundy=> INSERT INTO shoelace_data VALUES al_bundy-> (sl5, 4, brown, 1.0 , m); al_bundy=> INSERT INTO shoelace_data VALUES al_bundy-> (sl6, 0, brown, 0.9 , m); al_bundy=> INSERT INTO shoelace_data VALUES al_bundy-> (sl7, 7, brown, 60 , cm); al_bundy=> INSERT INTO shoelace_data VALUES al_bundy-> (sl8, 1, brown, 40 , inch); al_bundy=> al_bundy=> SELECT * FROM shoelace; sl_name |sl_avail|sl_color |sl_len|sl_unit |sl_len_cm -------+-----+-------+----+-----+-----sl1 | 5|black | 80|cm | 80 sl2 | 6|black | 100|cm | 100 sl7 | 7|brown | 60|cm | 60 sl3 | 0|black | 35|inch | 88.9 sl4 | 8|black | 40|inch | 101.6 sl8 | 1|brown | 40|inch | 101.6 sl5 | 4|brown | 1|m | 100 sl6 | 0|brown | 0.9|m | 90 (8 rows)
51
Esta es la SELECT ms sencilla que Al puede hacer en sus vistas, de modo que nosotros la tomaremos para explicar la base de las reglas de las vistas. SELECT * FROM shoelace fue interpretado por el traductor y produjo un rbol de traduccin.
SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace shoelace;
y este se le d al sistema de reglas. El sistema de reglas viaja a travs de la tabla de rango, y comprueba si hay reglas en pg_rewrite para alguna relacin. Cuando se procesa las entradas en la tabla de rango para shoelace (el nico hasta ahora) encuentra la regla _RETshoelace con el rbol de traduccin
SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, float8mul(s.sl_len, u.un_fact) AS sl_len_cm FROM shoelace *OLD*, shoelace *NEW*, shoelace_data s, unit u WHERE bpchareq(s.sl_unit, u.un_name);
Ntese que el traductor cambi el calculo y la cualicacin en llamadas a las funciones apropiadas. Pero de hecho esto no cambia nada. El primer paso en la reescritura es mezclar las dos tablas de rango. El rbol de traduccin entonces lee
SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace shoelace, shoelace *OLD*, shoelace *NEW*, shoelace_data s, unit u;
52
shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace shoelace, shoelace *OLD*, shoelace *NEW*, shoelace_data s, unit u WHERE bpchareq(s.sl_unit, u.un_name);
Y en el paso 3, reemplaza todas las variables en el arbol de traduccin, que se reeren a entradas de la tabla de rango (la nica que se est procesando en este momento para shoelace) por las correspondientes expresiones de la lista objetivo correspondiente a la accin de las reglas. El resultado es la query nal:
SELECT s.sl_name, s.sl_avail, s.sl_color , s.sl_len, s.sl_unit, float8mul(s.sl_len, u.un_fact) AS sl_len_cm FROM shoelace shoelace, shoelace *OLD*, shoelace *NEW*, shoelace_data s, unit u WHERE bpchareq(s.sl_unit, u.un_name);
Para realizar esta salida en una instruccin SQL real, un usuario humano debera teclear:
SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm FROM shoelace_data s, unit u WHERE s.sl_unit = u.un_name;
Esta ha sido la primera regla aplicada. Mientras se iba haciendo esto, la tabla de rango iba creciendo. De modo que el sistema de reglas contina comprobando las entradas de la tabla de rango. Lo siguiente es el el nmero 2 (shoelace *OLD*). La Relacin shoelace tiene una regla, pero su entrada en la tabla de rangos no est referenciada en ninguna de las variables del rbol de traduccin, de modo que se ingnora. Puesto que todas las entradas restantes en la tabla de rango, o bien no tienen reglas en pg_rewrite o bien no han sido referenciadas, se alcanza el nal de la tabla de rango.
53
La reescritura est completa y el resultado nal dado se pasa al optimizador. El optimizador ignora las entradas extra en la tabla de rango que no estn referenciadas por variables en el rbol de traduccin, y el plan producido por el planicador/optimizador debera ser exactamente el mismo que si Al hubiese tecleado la SELECT anterior en lugar de la seleccin de la vista. Ahora enfrentamos a Al al problema de que los Blues Brothers aparecen en su tienda y quieren comprarse zapatos nuevos, y como son los Blues Brothers, quieren llevar los mismos zapatos. Y los quieren llevar inmediatamente, de modo que necesitan tambin cordones. Al necesita conocer los zapatos para los que tiene en el almacn cordones en este momento (en color y en tamao), y adems para los que tenga un nmero igual o superior a 2. Nosotros le enseamos a realizar la consulta a su base de datos:
al_bundy=> SELECT * FROM shoe_ready WHERE total_avail >= 2; shoename |sh_avail|sl_name |sl_avail|total_avail -------+-----+-------+-----+------sh1 | 2|sl1 | 5| 2 sh3 | 4|sl7 | 7| 4 (2 rows)
Al es un guru de los zapatos, y sabe que slo los zapatos de tipo sh1 le sirven (los cordones sl7 son marrones, y los zapatos que necesitan cordones marrones no son los ms adecuados para los Blues Brothers). La salida del traductor es esta vez el arbol de traduccin.
SELECT shoe_ready.shoename, shoe_ready.sh_avail, shoe_ready.sl_name, shoe_ready.sl_avail, shoe_ready.total_avail FROM shoe_ready shoe_ready WHERE int4ge(shoe_ready.total_avail, 2);
Esa ser la primera regla aplicada para la relacin shoe_ready y da como resultado el rbol de traduccin
SELECT rsh.shoename,
54
rsh.sh_avail, rsl.sl_name, rsl.sl_avail, min(rsh.sh_avail, rsl.sl_avail) AS total_avail FROM shoe_ready shoe_ready, shoe_ready *OLD*, shoe_ready *NEW*, shoe rsh, shoelace rsl WHERE int4ge(min(rsh.sh_avail, rsl.sl_avail), 2) AND (bpchareq(rsl.sl_color, rsh.slcolor) AND float8ge(rsl.sl_len_cm, rsh.slminlen_cm) AND float8le(rsl.sl_len_cm, rsh.slmaxlen_cm) );
En realidad, la clausula AND en la cualicacin ser un nodo de operadores de tipo AND, con una expresin a la izquierda y otra a la derecha. Pero eso la hace menos legible de lo que ya es, y hay ms reglas para aplicar. De modo que slo las mostramos entre parntesis para agruparlos en unidades lgicas en el orden en que se aaden, y continuamos con las reglas para la relacin shoe como est en la entrada de la tabla de rango a la que se reere, y tiene una regla. El resultado de aplicarlo es
SELECT sh.shoename, sh.sh_avail, rsl.sl_name, rsl.sl_avail, min(sh.sh_avail, rsl.sl_avail) AS total_avail, FROM shoe_ready shoe_ready, shoe_ready *OLD*, shoe_ready *NEW*, shoe rsh, shoelace rsl, shoe *OLD*, shoe *NEW*, shoe_data sh, unit un WHERE (int4ge(min(sh.sh_avail, rsl.sl_avail), 2) AND (bpchareq(rsl.sl_color, sh.slcolor ) AND float8ge(rsl.sl_len_cm, float8mul(sh.slminlen, un.un_fact))
55
Y nalmente aplicamos la regla para shoelace que ya conocemos bien (esta vez en un arbol de traduccin que es un poco ms complicado) y obtenemos
SELECT sh.shoename, sh.sh_avail, s.sl_name, s.sl_avail, min(sh.sh_avail, s.sl_avail) AS total_avail FROM shoe_ready shoe_ready, shoe_ready *OLD*, shoe_ready *NEW*, shoe rsh, shoelace rsl, shoe *OLD*, shoe *NEW*, shoe_data sh, unit un, shoelace *OLD*, shoelace *NEW*, shoelace_data s, unit u WHERE ( (int4ge(min(sh.sh_avail, s.sl_avail), 2) AND (bpchareq(s.sl_color , sh.slcolor) AND float8ge(float8mul(s.sl_len, u.un_fact), float8mul(sh.slminlen, un.un_fact)) AND float8le(float8mul(s.sl_len, u.un_fact), float8mul(sh.slmaxlen, un.un_fact)) ) ) AND bpchareq(sh.slunit, un.un_name) ) AND bpchareq(s.sl_unit, u.un_name);
Lo reducimos otra vez a una instruccin SQL real que sea equivalente en la salida nal del sistema de reglas:
SELECT sh.shoename, sh.sh_avail, s.sl_name, s.sl_avail, min(sh.sh_avail, s.sl_avail) AS total_avail
56
shoe_data sh, shoelace_data s, unit u, unit un min(sh.sh_avail, s.sl_avail) >= 2 s.sl_color = sh.slcolor s.sl_len * u.un_fact >= sh.slminlen * un.un_fact s.sl_len * u.un_fact <= sh.slmaxlen * un.un_fact sh.sl_unit = un.un_name s.sl_unit = u.un_name;
El procesado recursivo del sistema de reglas reescribi una SELECT de una vista en un rbol de traduccin que es equivalente a exactamente lo que Al hubiese tecleado de no tener vistas.
Nota: Actualmente no hay mecanismos de parar la recursin para las reglas de las vistas en el sistema de reglas (slo para las otras reglas). Esto no es muy grave, ya que la nica forma de meterlo en un bucle sin n (bloqueando al cliente hasta que lea el limite de memoria) es crear tablas y luego crearles reglas a mano con CREATE RULE de forma que una lea a la otra y la otra a la una. Esto no puede ocurrir con el comando CREATE VIEW, porque en la primera creacin de una vista la segunda an no existe, de modo que la primera vista no puede seleccionar desde la segunda.
57
Cualquier otra cosa es absolutamente igual. Por ello, teniendo dos tablas t1 y t2, con atributos a y b, los rboles de traduccin para las dos instrucciones:
SELECT t2.b FROM t1, t2 WHERE t1.a = t2.a; UPDATE t1 SET b = t2.b WHERE t1.a = t2.a;
Las tablas de rango contienen entradas para las tablas t1 y t2. Las listas objetivo continen una variable que apunta al atributo b de la entrada de la tabla rango para la tabla t2. Las expresiones de cualicacin comparan los atributos a de ambos rangos para la igualdad.
La consecuencia es que ambos rboles de traduccin dan lugar a planes de ejecucin similares. En ambas hay joins entre las dos tablas. Para la UPDATE, las columnas que no aparecen de la tabla t1 son aadidas a la lista objetivo por el optimizador, y el rbol de traduccin nal se lee como:
UPDATE t1 SET a = t1.a, b = t2.b WHERE t1.a = t2.a;
Y por ello el ejecutor al correr sobre la join producir exactamente el mismo juego de resultados que
SELECT t1.a, t2.b FROM t1, t2 WHERE t1.a = t2.a;
Pero hay un pequeo problema con el UPDATE. El ejecutor no cuidar de que el resultado de la join sea coherente. El slo produce un juego resultante de las. La diferencia entre un comando SELECT y un comando UPDATE la manipula el llamador (caller) del ejecutor. El llamador slo conoce (mirando en el rbol de traduccin) que esto es una UPDATE, y sabe que su resultado deber ir a la tabla t1. Pero cul de las 666 las que hay debe ser reemplazada por la nueva la? El plan ejecutado es una join
58
con una cualicacin que potencialmente podra producir cualquier nmero de las entre 0 y 666 en un nmero desconocido. Para resolver este problema, se aade otra entrada a la lista objetivo en las instrucciones UPDATE y DELETE. Es el identicador de tupla actual (current tuple id, ctid). Este es un atributo de sistema con caractersticas especiales. Contiene el bloque y posicin en el bloque para cada la. Conociendo la tabla, el ctid puede utilizarse para encontrar una la especca en una tabla de 1.5 GB que contiene millones de las atacando un nico bloque de datos. Tras la adicin del ctid a la lista objetivo, el juego de resultados nal se podra denir como
SELECT t1.a, t2.b, t1.ctid FROM t1, t2 WHERE t1.a = t2.a;
Entra ahora en funcionamiento otro detalle de >Postgres. Las las de la tabla no son reescritas en este momento, y el por ello por lo que ABORT TRANSACTION es muy rpido. En una Update, la nueva la resultante se inserta en la tabla (tras retirarle el ctid) y en la cabecera de la tupla de la la cuyo ctid apuntaba a las entradas cmax y zmax, se ja el contador de comando actual y el identicador de transaccion actual (ctid). De este modo, la la anterior se oculta tras el commit de la transaccin, y el limpiador vacuum puede realmente eliminarla. Conociendo todo eso, podemos simplemente aplicar las reglas de las vistas exactamente en la misma forma en cualquier comando. No hay diferencia.
8.2.4.1. Benecios
Los benecios de implementar las vistas con el sistema de reglas estn en que el
59
optimizados tiene toda la informacin sobre qu tablas tienen que ser revisadas, ms las relaciones entre estas tablas, ms las cualicaciones restrictivas a partir de la denicin de las vistas, ms las cualicaciones de la query original, todo en un nico rbol de traduccin. Y esta es tambin la situacin cuando la query original es ya una join entre vistas. Ahora el optimizador debe decidir cul es la mejor ruta para ejecutar la query. Cuanta ms informacin tenga el optimizador, mejor ser la decisin. Y la forma en que se implementa el sistema de reglas en Postgres asegura que toda la informacin sobre la query est utilizable.
Las vistas con columnas agregadas tienen malos problemas. Las expresiones agregadas en las cualicaciones deben utilizarse en subselects. Actualmente no es posible hacer una join de dos vistas en las que cada una de ellas tenga una columna agregada, y comparar los dos valores agregados en a cualicacin. Mientras tanto, es posible colocar estas expresiones agregadas en funciones con los argumentos apropiados y utilizarlas en la denicin de las vistas.
60
Las vistas de uniones no son soportadas. Ciertamente es sencillo reescribir una SELECT simple en una unin, pero es un poco ms dicil si la vista es parte de una join que hace una UPDATE. Las clausulas ORDER BY en las deniciones de las vistas no estn soportadas. DISTINCT no est soportada en las deniciones de vistas.
No hay una buena razon por la que el optimizador no debiera manipular construcciones de rboles de traduccin que el traductor nunca podra producir debido a las limitaciones de la sintaxis de SQL. El autor se alegrar de que estas limitaciones desaparezcan en el futuro.
Lo interesante es que el cdigo de retorno para la INSERT nos di una identicacin de objeto, y nos dijo que se ha insertado una la. Sin embargo no aparece en shoe_data. Mirando en el directorio de la base de datos, podemos ver que el chero de la base de datos para la relacin de la vista shoe parece tener ahora un bloque de datos. Y efectivamente es as.
61
Podemos tambin intentar una DELETE, y si no tiene una cualicacin, nos dir que las las se han borrado y la siguiente ejecucin de vacuum limpiar el chero hasta tamao cero. La razon para este comportamiento es que el rbol de la traduccin para la INSERT no hace referencia a la relacin shoe en ninguna variable. La lista objetivo contiene slo valores constantes. Por ello no hay reglas que aplicar y se mantiene sin cambiar hasta la ejecucin, insertandose la la. Del mismo modo para la DELETE. Para cambiar esto, podemos denir reglas que modiquen el comportamiento de las queries no-SELECT. Este es el tema de la siguiente seccin.
Pueden no tener accin. Pueden tener mltiples acciones. La palabra clave INSTEAD es opcional. Las pseudo-relaciones NEW y OLD se vuelven utilizables. Puede haber cualicaciones a las reglas.
Segundo, no modican el rbol de traduccin en el sitio. En lugar de ello, crean cero o varios rboles de traduccin nuevos y pueden desechar el original.
62
En lo que sigue, "las reglas de update" muestran reglas que estn denidas ON INSERT, UPDATE o DELETE. Update toma las reglas aplicadas por el sistema de reglas cuando la relacin resultado y el tipo de comando de un rbol de traduccin son iguales al objeto y el acontecimiento dado en el comando CREATE RULE. Para reglas de update, el sistema de reglas crea una lista de rboles de traduccin. Inicialmente la lista de rboles de traduccin est vaca. Puede haber cero (palabra clave NOTHING), una o mltiples acciones. Para simplicar, veremos una regla con una accin. Esta regla puede tener una cualicacin o no y puede ser INSTEAD o no. Qu es una cualicacin de una regla? Es una restriccin que se dice cundo las acciones de una regla se deberan realizar y cundo no. Esta cualcacin slo se puede referir a las pseudo-relaciones NEW y/o OLD, que bsicamente son la relacin dada como objeto (pero con unas caractersticas especiales). De este modo tenemos cuatro casos que producen los siguientes rboles de traduccin para una regla de una accin: Sin cualicacin ni INSTEAD:
El rbol de traduccin para la accin de la regla a la que se ha aadido cualicacin a los rboles de traduccin originales.
El rbol de traduccin para la accin de la regla a la que se ha aadido cualicacin a los rboles de traduccin originales.
63
Se da cualicacin y no se da INSTEAD:
El rbol de traduccin de la accin de la regla, a la que se han aadido la cualicacin de la regla y la cualicacin de los rboles de traduccin originales.
Se da cualicacin y se da INSTEAD:
El rbol de traduccin de la accin de la regla a la que se han aadido la cualicacin de la regla y la cualicacin de los rboles de traduccin originales. El rbol de traduccin original al que se le ha aadido la cualicacin de la regla negada.
Finalmente, si la regla no es INSTEAD, el rbol de traduccin original sin cambiar se aade a la lista. Puesto que slo las reglas INSTEAD cualicadas se aaden al rbol de traduccin original, terminamos con un mximo total de dos rboles de traduccin para una regla con una accin. Los rboles de traduccin generados a partir de las acciones de las reglas se colocan en el sistema de reescritura de nuevo, y puede ser que otras reglas aplicadas resulten en ms o menos rboles de traduccin. De este modo, los rboles de traduccin de las acciones de las reglas deberan tener bien otro tipo de comando, bien otra relacin resultado. De otro modo, este proceso recursivo terminara en un bucle. Hay un lmite de recursiones compiladas actualmente de 10 iteraciones. Si tras 10 iteraciones an sigue habiendo reglas de update para aplicar, el sistema de reglas asumir que se ha producido un bucle entre muchas deniciones de reglas y aborta la transaccin. Los rboles de traduccin encontrados en las acciones del catlogo de sistema pg_rewrite son slo plantillas. Una vez que ellos pueden hacer referencia a las entradas de tabla de rango para NEW u OLD, algunas sustituciones habrn sido hechas antes de ser utilizadas. Para cualquier referencia a NEW, la lista objetivo de la query original se revisa busando una entrada correspondiente. si se encuentra, esas entradas de la expresin se sitan en la referencia. De otro modo, NEW se mantiene igual que OLD. Cualquier referencia a OLD se reemplaza por una referencia a la entrada de la tabla de rango que es la relacin resultado.
64
CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data WHERE NEW.sl_avail != OLD.sl_avail DO INSERT INTO shoelace_log VALUES ( NEW.sl_name, NEW.sl_avail, getpgusername(), now::text );
Un detalle interesante es la caracterizacin de now en la reglas de la accin INSERT para teclear texto. Sin ello, el traductor vera en el momento del CREATE RULE, que el tipo objetivo en shoelace_log es un dato de tipo fecha, e intenta hacer una constante de l... con xito. De ese modo, se almacenara un valor constante en la accin de la regla y todas las entradas del log tendran la hora de la instruccin CREATE RULE. No es eso exactamente lo que queremos. La caracterizacin lleva al traductor a construir un "fecha-hora" que ser evaluada en el momento de la ejecucin (datetime(now::text)). Ahora Al hace
al_bundy=> UPDATE shoelace_data SET sl_avail = 6 al_bundy-> WHERE sl_name = sl7;
65
sl_name |sl_avail|log_who|log_when -------+-----+-----+--------------------sl7 | 6|Al |Tue Oct 20 16:14:45 1998 MET DST (1 row)
Que es justo lo que nosotros esperbamos. Veamos qu ha ocurrido en la sombra. El traductor cre un rbol de traduccin (esta vez la parte del rbol de traduccin original est resaltado porque la base de las operacin es es la accin de la regla para las reglas de update)
UPDATE shoelace_data SET sl_avail = 6 FROM shoelace_data shoelace_data WHERE bpchareq(shoelace_data.sl_name, sl7);
Hay una regla para log_shoelace que es ON UPDATE con la expresin de cualicacin de la regla:
int4ne(NEW.sl_avail, OLD.sl_avail)
y una accin
INSERT INTO shoelace_log SELECT *NEW*.sl_name, *NEW*.sl_avail, getpgusername(), datetime(now::text) FROM shoelace_data *NEW*, shoelace_data *OLD*, shoelace_log shoelace_log;
No detallaremos la salida de la vista del sistema pg_rules. Especialmente manipula la siutacin de que aqu slo se haga referencia a NEW y OLD en la INSERT, y las salidas del formato de VALUES de INSERT. De hecho, no hay diferencia entre una INSERT ... VALUES y una INSERT ... SELECT al nivel del rbol de traduccin. Ambos tienen tablas de rango, listas objetivo, pueden tener cualicacin, etc. El optimizador decide ms tarde si crear un plan de ejecucin de tio resultado, barrido secuencial, barrido de ndice, join o cualquier otro para ese rbol de traduccin. Si no hay referencias en entradas de la tabla de rango previas al rbol de traduccin, ste se convierte en un plan de ejecucin (la versin INSERT ... VALUES). La accin de las reglas anterior puede ciertamente resultar en ambas variantes.
66
La regla es una regla no-INSTEAD cualicada, de modo que el sistema de reglas deber devolver dos rboles de traduccin. La accin de la regla modicada y el rbol de traduccin original. En el primer paso, la tabla de rango de la query original est incorporada al rbol de traduccin de la accin de las reglas. Esto da como resultado
INSERT INTO shoelace_log SELECT *NEW*.sl_name, *NEW*.sl_avai, getpgusername(), datetime(now::text) FROM shoelace_data shoelace_data, shoelace_data *NEW*, shoelace_data *OLD*, shoelace_log shoelace_log;
En el segundo paso, se aade la cualicacin de la regla, de modo que el resultado se restringe a las las en las que sl_avail cambie.
INSERT INTO shoelace_log SELECT *NEW*.sl_name, *NEW*.sl_avai, getpgusername(), datetime(now::text) FROM shoelace_data shoelace_data, shoelace_data *NEW*, shoelace_data *OLD*, shoelace_log shoelace_log WHERE int4ne(*NEW*.sl_avail, *OLD*.sl_avail);
En el tercer paso, se aade la cualicacin de los rboles de traduccin originales, restringiendo el juego de resultados ms an, a slo las las tocadas por el rbol de traduccin original.
INSERT INTO shoelace_log SELECT *NEW*.sl_name, *NEW*.sl_avai, getpgusername(), datetime(now::text) FROM shoelace_data shoelace_data, shoelace_data *NEW*, shoelace_data *OLD*, shoelace_log shoelace_log WHERE int4ne(*NEW*.sl_avail, *OLD*.sl_avail) AND bpchareq(shoelace_data.sl_name, sl7);
67
En el paso cuatro se sustituyen las referencias NEW por las entradas de la lista objetivo del rbol de traduccin original o con las referencias a variables correspondientes de la relacin resultado.
INSERT INTO shoelace_log SELECT shoelace_data.sl_name, 6, getpgusername(), datetime(now::text) FROM shoelace_data shoelace_data, shoelace_data *NEW*, shoelace_data *OLD*, shoelace_log shoelace_log WHERE int4ne(6, *OLD*.sl_avail) AND bpchareq(shoelace_data.sl_name, sl7);
Y esto es. De modo que la mxima reduccin de la salida del sistema de reglas es una lista de dos rboles de traduccin que son lo mismo que las instrucciones:
INSERT INTO shoelace_log SELECT shoelace_data.sl_name, 6, getpgusername(), now FROM shoelace_data WHERE 6 != shoelace_data.sl_avail AND shoelace_data.sl_name = sl7; UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = sl7;
68
Estas con ejecutadas en este orden y eso es exactamente lo que la regla dene. Las sustituciones y las cualicaciones aadidas aseguran que si la query original fuese una
UPDATE shoelace_data SET sl_color = green WHERE sl_name = sl7;
no se habra escrito ninguna entrada en la tabla de log, ya que esta vez el rbol de traduccin original no contiene una entrada de la lista objetivo para sl_avail, de modo que NEW.sl_avail ser reemplazada por shoelace_data.sl_avail resultando en la query adicional
INSERT INTO shoelace_log SELECT shoelace_data.sl_name, shoelace_data.sl_avail, getpgusername(), now FROM shoelace_data WHERE shoelace_data.sl_avail != shoelace_data.sl_avail AND shoelace_data.sl_name = sl7;
cuya cualicacin nunca ser cierta. Una vez que no hay diferencias a nivel de rbol de traduccin entre una INSERT ... SELECT, y una INSERT ... VALUES, trabajar tambin si la query original modicaba multiples columnas. De modo que si Al hubiese pedido el comando
UPDATE shoelace_data SET sl_avail = 0 WHERE sl_color = black;
sern actualizadas cuatro las (sl1, sl2, sl3 y sl4). Pero sl3 ya tiene sl_avail = 0. Esta vez, la cualicacin de los rboles de traduccin originales es diferente y como resultado tenemos el rbol de traduccin adicional
INSERT INTO shoelace_log SELECT shoelace_data.sl_name, 0, getpgusername(), now FROM shoelace_data WHERE 0 != shoelace_data.sl_avail
69
Este rbol de traduccin seguramente insertar tres nuevas entradas de la tabla de log. Y eso es absoltamente correcto. Es importante recordar que el rbol de traduccin original se ejecuta el ltimo. El "agente de trco" de Postgres incrementa el contador de comandos entre la ejecucin de los dos rboles de traduccin, de modo que el segundo puede ver cambios realizados por el primero. Si la UPDATE hubiera sido ejecutada primero, todas las las estaran ya a 0, de modo que la INSERT del logging no habra encontrado ninguna la para las que shoelace_data.sl_avail != 0: no habra dejado ningn rastro.
Si Al ahora intenta hacer cualquiera de estas operaciones en la relacin vista shoe, el sistema de reglas aplicar las reglas. Una vez que las reglas no tiene acciones y son INSTEAD, la lista resultante de rboles de traduccin estar vaca, y la query no devolver nada, debido a que no hay nada para ser optimizado o ejecutado tras la actuacin del sistema de reglas.
Nota: Este hecho debera irritar a las aplicaciones cliente, ya que no ocurre absoltamente nada en la base de datos, y por ello, el servidor no devuelve nada
70
para la query. Ni siquiera un PGRES_EMPTY_QUERY o similar ser utilizable en libpq. En psql, no ocurre nada. Esto debera cambiar en el futuro.
Una forma ms sosticada de utilizar el sistema de reglas es crear reglas que reescriban el rbol de traduccin en uno que haga la operacin correcta en las tablas reales. Para hacer esto en la vista shoelace, crearemos las siguientes reglas:
CREATE RULE shoelace_ins AS ON INSERT TO shoelace DO INSTEAD INSERT INTO shoelace_data VALUES ( NEW.sl_name, NEW.sl_avail, NEW.sl_color, NEW.sl_len, NEW.sl_unit); CREATE RULE shoelace_upd AS ON UPDATE TO shoelace DO INSTEAD UPDATE shoelace_data SET sl_name = NEW.sl_name, sl_avail = NEW.sl_avail, sl_color = NEW.sl_color, sl_len = NEW.sl_len, sl_unit = NEW.sl_unit WHERE sl_name = OLD.sl_name; CREATE RULE shoelace_del AS ON DELETE TO shoelace DO INSTEAD DELETE FROM shoelace_data WHERE sl_name = OLD.sl_name;
Ahora llega un paquete de cordones de zapatos a la tienda de Al, y el tiene una gran lista de artculos. Al no es particularmente bueno haciendo clculos, y no lo queremos actualizando manualmente la vista shoelace. En su lugar, creamos dos tablas pequeas,
71
una donde l pueda insertar los datos de la lista de artculos, y otra con un truco especial. Los comandos CREATE completos son:
CREATE TABLE shoelace_arrive ( arr_name char(10), arr_quant integer ); CREATE TABLE shoelace_ok ( ok_name char(10), ok_quant integer ); CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = sl_avail + NEW.ok_quant WHERE sl_name = NEW.ok_name;
Que es exactametne lo que haba en la lista de artculos. Daremos una rpida mirada en los datos actuales.
al_bundy=> SELECT * FROM shoelace ORDER BY sl_name; sl_name |sl_avail|sl_color |sl_len|sl_unit |sl_len_cm -------+-----+-------+----+-----+-----sl1 | 5|black | 80|cm | 80 sl2 | 6|black | 100|cm | 100 sl7 | 6|brown | 60|cm | 60
72
| | | | |
| | | | |
| | | | |
Esta es una larga va desde la primera INSERT ... SELECT a estos resultados. Y su descripcin ser la ltima en este documento (pero no el ltimo ejemplo :-). Primero estaba la salida de los traductores:
73
INSERT INTO shoelace_ok SELECT shoelace_arrive.arr_name, shoelace_arrive.arr_quant FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok;
y lanza otra vez la INSERT original sobre shoelace_ok. Esta query reescrita se pasa al sistema de reglas de nuevo, y la aplicacin de la segunda regla shoelace_upd produce
UPDATE shoelace_data SET sl_name = shoelace.sl_name, sl_avail = int4pl(shoelace.sl_avail, shoelace_arrive.arr_quant), sl_color = shoelace.sl_color, sl_len = shoelace.sl_len, sl_unit = shoelace.sl_unit FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok, shoelace_ok *OLD*, shoelace_ok *NEW*, shoelace shoelace, shoelace *OLD*, shoelace *NEW*, shoelace_data showlace_data WHERE bpchareq(shoelace.sl_name, showlace_arrive.arr_name) AND bpchareq(shoelace_data.sl_name, shoelace.sl_name);
Otra vez es una regla INSTEAD, y el rbol de traduccin anterior se deshecha. Ntese que esta query an utiliza la vista shoelace. Pero el sistema de reglas no ha terminado con esta vuelta, de modo que contina y aplica la regla _RETshoelace, produciendo
UPDATE shoelace_data SET sl_name = s.sl_name, sl_avail = int4pl(s.sl_avail, shoelace_arrive.arr_quant),
74
sl_color = s.sl_color, sl_len = s.sl_len, sl_unit = s.sl_unit FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok, shoelace_ok *OLD*, shoelace_ok *NEW*, shoelace shoelace, shoelace *OLD*, shoelace *NEW*, shoelace_data showlace_data, shoelace *OLD*, shoelace *NEW*, shoelace_data s, unit u WHERE bpchareq(s.sl_name, showlace_arrive.arr_name) AND bpchareq(shoelace_data.sl_name, s.sl_name);
De nuevo se ha aplicado una regla de update y por ello vuelve a girar la rueda, y llegamos a la ronda de reescritura nmero 3. Esta vez, se aplica la regla log_shoelace, que produce el rbol de traduccin extra
INSERT INTO shoelace_log SELECT s.sl_name, int4pl(s.sl_avail, shoelace_arrive.arr_quant), getpgusername(), datetime(now::text) FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok, shoelace_ok *OLD*, shoelace_ok *NEW*, shoelace shoelace, shoelace *OLD*, shoelace *NEW*, shoelace_data showlace_data, shoelace *OLD*, shoelace *NEW*, shoelace_data s, unit u, shoelace_data *OLD*, shoelace_data *NEW* shoelace_log shoelace_log WHERE bpchareq(s.sl_name, showlace_arrive.arr_name) AND bpchareq(shoelace_data.sl_name, s.sl_name); AND int4ne(int4pl(s.sl_avail, shoelace_arrive.arr_quant), s.sl_avail);
Tras de lo cual, el sistema de reglas se desconecta y devuelve los rboles de traduccin generados. De esta forma, terminamos con dos rboles de traduccin nales que son iguales a las instrucciones de SQL
75
INSERT INTO shoelace_log SELECT s.sl_name, s.sl_avail + shoelace_arrive.arr_quant, getpgusername(), now FROM shoelace_arrive shoelace_arrive, shoelace_data shoelace_data, shoelace_data s WHERE s.sl_name = shoelace_arrive.arr_name AND shoelace_data.sl_name = s.sl_name AND s.sl_avail + shoelace_arrive.arr_quant != s.sl_avail; UPDATE shoelace_data SET sl_avail = shoelace_data.sl_avail + shoelace_arrive.arr_quant FROM shoelace_arrive shoelace_arrive, shoelace_data shoelace_data, shoelace_data s WHERE s.sl_name = shoelace_arrive.sl_name AND shoelace_data.sl_name = s.sl_name;
El resultado es que los datos vienen de una relacin, se insertan en otra, cambian por actualizaciones una tercera, cambian por actualizaciones una cuarta, ms registran esa actualizacin nal en una quinta: todo eso se reduce a dos queries. Hay un pequeo detalle un tanto desagradable. Mirando en las dos queries, descrubrimos que la relacin shoelace_data aparece dos veces en la tabla de rango, lo que se debera reducir a una sla. El optimizador no manipula esto, y por ello el plan de ejecucin para la salida del sistema de reglas de la INSERT ser
Nested Loop -> Merge Join -> Seq Scan -> Sort -> Seq Scan on s -> Seq Scan -> Sort -> Seq Scan on shoelace_arrive -> Seq Scan on shoelace_data
76
Seq Scan on s
que produce exactamente las mismas entradas en la relacin de log. Es decir, el sistema de regls ha probocado un barrido extra de la relacin shoelace_data absoltamente innecesario. Y el mismo barrido obsoleto se produce de nuevo en la UPDATE. Pero era un trabajo realmente duro hacer que todo sea posible. Una demostracin nal del sistema de reglas de Postgres y de su poder. Hay una astuta rubia que vende cordones de zapatos. Y lo que Al nunca hubiese imaginado, ella no es slo astuta, tambin es elegante, un poco demasiado elegante. Por ello, ella se empea de tiempo en tiempo en que Al pida cordones que son absoltamente invendibles. Esta vez ha pedido 1000 pares de cordones magenta, y aunque ahora no es posible adquirir otro color, como l se comprometi a comprar algo, prepara su base de datos para cordones rosa.
al_bundy=> INSERT INTO shoelace VALUES al_bundy-> (sl9, 0, pink, 35.0, inch, 0.0); al_bundy=> INSERT INTO shoelace VALUES al_bundy-> (sl10, 1000, magenta, 40.0, inch, 0.0);
Ahora quiere revisar los cordones que no casan con ningn par de zapatos. El podra realizar una complicada query cada vez, o bien le podemos preparar una vista al efecto:
CREATE VIEW shoelace_obsolete AS SELECT * FROM shoelace WHERE NOT EXISTS (SELECT shoename FROM shoe WHERE slcolor = sl_color);
cuya salida es
77
al_bundy=> SELECT * FROM shoelace_obsolete; sl_name |sl_avail|sl_color |sl_len|sl_unit |sl_len_cm -------+-----+-------+----+-----+-----sl9 | 0|pink | 35|inch | 88.9 sl10 | 1000|magenta | 40|inch | 101.6
Sobre los 1000 cordones magenta, deberamos avisar a Al antes de que podamos hacerlo de nuevo, pero ese es otro problema. La entrada rosa, la borramos. Para hacerlo un poco ms dicil para Postgres, no la borramos directamente. En su lugar, crearemos una nueva vista
CREATE VIEW shoelace_candelete AS SELECT * FROM shoelace_obsolete WHERE sl_avail = 0;
Voila:
al_bundy=> SELECT * FROM shoelace; sl_name |sl_avail|sl_color |sl_len|sl_unit -------+-----+-------+----+-----+-----sl1 | 5|black | 80|cm sl2 | 6|black | 100|cm sl7 | 6|brown | 60|cm sl4 | 8|black | 40|inch sl3 | 10|black | 35|inch sl8 | 21|brown | 40|inch sl10 | 1000|magenta | 40|inch sl5 | 4|brown | 1|m sl6 | 20|brown | 0.9|m (9 rows)
Una DELETE en una vista, con una subselect como cualicacin, que en total utiliza 4 vistas anidadas/cruzadas, donde una de ellas mismas tiene una subselect de
78
cualicacin conteniendo una vista y donde se utilizan columnas calculadas queda reescrita en un nico rbol de traduccin que borra los datos requeridos de una tabla real. Pienso que hay muy pocas ocasiones en el mundo real en las que se una construccin similar sea necesaria. Pero me tranquiliza un poco que esto funcione.
La verdad es: Haciendo esto encontr otro bug mientras escriba este documento. Pero tras jarlo comprob un poco avergonzado que trabajaba correctamente.
79
SELECT person, phone FROM phone_data WHERE NOT private; GRANT SELECT ON phone_number TO secretary;
Nadie excepto l, y el superusuario de la base de datos, pueden acceder a la tabla phone_data. Pero debido a la GRANT, la secretaria puede SELECT a travs de la vista phone_numbre. El sistema de reglas reescribir la SELECT de phone_numbre en una SELECT de phone_data y aade la cualicacin de que slo se buscan las entradas cuyo "privado" sea falso. Una vez que el usuario sea el propietario de phone_numbre, la lectura accede a phone_data se comprueba contra sus permisos, y la query se considera autorizada. La comprobacin para acceder a phone_number se realiza entonces, de modo que nadie ms que la secretaria pueda utilizarlo. Los permisos son comprobados regla a regla. De modo que la secretaria es ahora la nica que puede ver los nmeros de telfono pblicos. Pero la secretaria puede crear otra vista y autorizar el acceso a ella al pblico. Entonces, cualquiera puede ver los datos de phone_numbre a travs de la vista de la secretaria. Lo que la secretaria no puede hacer es crear una vista que acceda directamente a phone_data (realmente si puede, pero no trabajar, puesto que cada acceso abortar la transaccin durante la comprobacin de los permisos). Y tan pronto como el usuario tenga noticia de que la secretaria ha abierto su vista a phone_numbre, el puede REVOKE su acceso. Inmediatamente despus, cualquier acceso a la vista de las secretarias fallar. Alguien podra pensar que este chequeo regla a regla es un agujero de seguridad, pero de hecho no lo es. Si esto no trabajase, la secretaria podra generar una tabla con las mismas columnas de phone_number y copiar los datos aqu todos los das. En este caso seran ya sus propios datos, y podra autorizar el acceso a cualquiera que ella quisiera. Un GRANT quiere decir "Yo Confo en T". Si alguien en quien conamos hace lo anterior, es el momento de volver sobre nuestros pasos, y hacer el REVOKE. Este mecanismo tambin trabaja para reglas de update. En el ejemplo de la seccin previa, el propietario de las tablas de la base de datos de Al (suponiendo que no fuera el mismo Al) podra haber autorizado (GRANT) SELECT, INSERT, UPDATE o DELETE a la vista shoelace a Al. Pero slo SELECT en shoelace_log. La accin de la regla de escribir entradas del log deber ser ejecutada con exito, y Al podra ver las entradas del log, pero el no puede crear nuevas entradas, ni podra manipular ni remover las existentes.
80
Atencin: GRANT ALL actualmente incluye permisos RULE. Esto permite al usuario autorizado borrar la regla, hacer los cambios y reinstalarla. Pienso que esto debera ser cambiado rpidamente.
81
- indexed - indexed
Ambas tablas tienen muchos millares de las y el ndice sobre hostname es nico. La columna hostname contiene el nombre de dominio cualicado completo del ordenador. La regla/trigger debera desencadenar el borrado de las de la tabla software que se reeran a un host borrado. Toda vez que el trigger se llama para cada la individual borrada de computer, se puede usar la instruccin
DELETE FROM software WHERE hostname = $1;
en un plan preparado y salvado, y pasar el hostname en el parmetro. La regla debera ser escrita como
CREATE RULE computer_del AS ON DELETE TO computer DO DELETE FROM software WHERE hostname = OLD.hostname;
Veremos ahora en que se diferencian los dos tipos de delete. En el caso de una
DELETE FROM computer WHERE hostname = mypc.local.net;
La tabla computer se revisa por ndice (rpido) y la query lanzada por el trigger tambin debera ser un barrido de ndice (rpido tambin). La query extra para la regla sera una
DELETE FROM software WHERE computer.hostname = mypc.local.net AND software.hostname = computer.hostname;
Puesto que se han creado los ndices apropiados, el optimizador crear un plan de
Nestloop -> Index Scan using comp_hostidx on computer -> Index Scan using soft_hostidx on software
82
De modo que no habra mucha diferencia de velocidad entre la implementacin del trigger y de la regla. Con la siguiente delete, queremos mostrar borrar los 2000 ordenadores cuyo hostname empieza con old. Hay dos posibles queries para hacer eso. Una es
DELETE FROM computer WHERE hostname >= old AND hostname < ole
Esto muestra que el optimizador no comprueba que la cualicacin sobre hostname en computer tambin debera se utilizado para un barrido por ndice en software donde hay mltiples expresiones de cualicacin combinadas con AND, que el hace en la versin regexp de la query. El trigger ser invocado una vez para cada una de los 2000 viejos ordenadores que sern borrados, lo que dar como resultado un barrido por ndice sobre computer y 2000 barridos por ndice sobre software. La implementacin de la regla lo har con dos queries sobre ndices. Y depender del tamao promedio de la tabla software si la regla ser ms rpida en una situacin de barrido secuencial. 2000 ejecuciones de queries sobre el gestor SPI toman su tiempo, incluso si todos los bloques del ndice se encuentran en la memora cach. La ltima query que veremos es
83
De nuevo esto debera dar como resultado muchoas las para borrar de computer. Por ello el trigger disparar de nuevo muchas queries sobre el ejecutor. Pero el plan de las reglas ser de nuevo un bucle anidado sobre dos barridos de ndice. Slo usando otro ndice en computer:
Nestloop -> Index Scan using comp_manufidx on computer -> Index Scan using soft_hostidx on software
En cualquiera de estos casos, las queries extra del sistema de reglas sern ms o menos independientes del nmero de las afectadas en la query. Otra situacin son los casos de UPDATE donde depende del cambio de un atributo si la accin debera realizarse o no. En la versin 6.4 de Postgres, la especicacin de atributos para acontencimientos de reglas se ha deshabilitado (y tendr su regreso en la 6.5, quiz antes permanezcan en antena!). De modo que por ahora la nica forma de crear una regla como en el ejemplo de shoelace_log es hacerlo con una cualcacin de la regla. Eso da como resultado una query adicional que se realiza siempre, incluso si el atributo que nos interesa no puede ser cambiado de ninguna forma porque no aparece en la lista objetivo de la query inicial. Cuando se habilite de nuevo, ser una nueva ventaja del sistema de reglas sobre los triggers. La optimizacin de un trigger deber fallar por denicin en este caso, porque el hecjo de que su accoin solo se har cuando un atributo especco sea actualizado, est oculto a su funcionalidad. La denicin de un trigger slo permite especicar el nivel de la, de modo que si se toca una la, el trigger ser llamado a hacer su trabajo. El sistema de reglas lo sabr mirndo la lista objetivo y suprimir la query adicional por completo si el atributo no se ha tocado. De modo que la regla, cualicada o no, slo har sus barridos si tiene algo que hacer. Las reglas slo sern signicativamente ms lentas que los triggers si sus acciones dan como resultado joins grandes y mal cualicadas, una situacin en la que falla el
84
optimizador. Tenemos un gran martillo. Utilizar un gran martillo sin cuidado puede causar un gran dao, pero dar el toque correcto, puede hundir cualquier clavo hasta la cabeza.
85
86
Atributo amorderstrategy
Descripcin cero si el ndice no ofrece secuencia de ordenamiento, sino el nmero de estrategia del operador de estrategia que describe la secuencia de ordenamiento
indicadores de procedimiento para las rutinas de interfaz con el mtodo de acceso. Por ejemplo, aqu aparecen identicadores regproc para abrir, cerrar y obtener instancias desde el mtodo de acceso
El identicador de objeto (object ID) de la instancia en pg_am se utiliza como una clave fornea en multitud de otras clases. No es necesario que Ud. agregue una nueva instancia en esta clase; lo que debe interesarle es el identicador de objeto (object ID) de la instancia del mtodo de acceso que quiere extender:
SELECT oid FROM pg_am WHERE amname = btree; +---+ |oid | +---+ |403 | +---+
Utilizaremos ese comando SELECT en una clusula WHERE posterior. El atributo amstrategies tiene como nalidad estandarizar comparaciones entre tipos de datos. Por ejemplo, los B-trees imponen un ordenamiento estricto en las claves, de menor a mayor. Como Postgres permite al usuario denir operadores, no puede, a travs del nombre del operador (por ej., ">" or "<"), identicar qu tipo de comparacin es. De hecho, algunos mtodos de acceso no imponen ningn
87
ordenamiento. Por ejemplo, los R-trees expresan una relacin de inclusin en un rectngulo, mientras que una estructura de datos de tipo hash expresa nicamente similaridad de bits basada en el valor de una funcin hash. Postgres necesita alguna forma consistente para interpretar los requisitos en sus consultas, identicando el operador y decidiendo si se puede utilizar un ndice existente. Esto implica que Postgres necesita conocer, por ejemplo, que los operadores "<=" y ">" particionan un B-tree. Postgres utiliza estrategias para expresar esas relaciones entre los operadores y las formas en que pueden utilizarse al recorrer los ndices. Denir un nuevo conjunto de estrategias est ms all del alcance de esta exposicin, pero explicaremos cmo funcionan las estrategias B-tree porque necesitar conocerlas para agregar una nueva clase de operador. En la clase pg_am, el atributo amstrategies es el nmero de estrategias denidas para este mtodo de acceso. Para los B-trees, este nmero es 5. Estas estrategias corresponden a Tabla 9-2. Estrategias B-tree Operacin menor que menor que o igual a igual mayor que o igual a mayor que ndice 1 2 3 4 5
La idea es que ser necesario agregar procedimientos correspondientes a las comparaciones mencionadas arriba a la tabla pg_amop (vase ms abajo). El cdigo de mtodo de acceso puede utilizar estos nmeros de estrategia, sin tener en cuenta el tipo de datos, para resolver cmo particionar el B-tree, calcular la selectividad, etctera. No se preocupe an acerca de los detalles para agregar procedimientos; slo comprenda que debe existir un conjunto de procedimientos para int2, int4, oid, y todos los dems tipos de datos donde puede operar un B-tree. Algunas veces, las estrategias no proporcionan la informacin suciente para resolver
88
la forma de utilizar un ndice. Algunos mtodos de acceso requieren otras rutinas de soporte para poder funcionar. Por ejemplo, el mtodo de acceso B-tree debe ser capaz de comparar dos claves y determinar si una es mayor que, igual a, o menor que la otra. De manera anloga, el mtodo de acceso R-tree debe ser capaz de calcular intersecciones, uniones, y tamaos de rectngulos. Estas operaciones no corresponden a requisitos del usuario en las consultas SQL; son rutinas administrativas utilizadas por los mtodos de acceso, internamente. Para manejar diversas rutinas de soporte consistentemente entre todos los mtodos de acceso de Postgres, pg_am incluye un atributo llamado amsupport. Este atributo almacena el nmero de rutinas de soporte utilizadas por un mtodo de acceso. Para los B-trees, este nmero es uno la rutina que toma dos claves y devuelve -1, 0, o +1, dependiendo si la primer clave es menor que, igual a, o mayor que la segunda.
Nota: En trminos estrictos, esta rutina puede devolver un nmero negativo (< 0), 0, o un valor positivo distinto de cero (> 0).
La entrada amstrategies en pg_am slo indica el nmero de estrategias denidas para el mtodo de acceso en cuestin. Los procedimientos para menor que, menor que o igual a, etctera no aparecen en pg_am. De manera similar, amsupport es solamente el nmero de rutinas de soporte que requiere el mtodo de acceso. Las rutinas reales estn listadas en otro lado. Adems, la entrada amorderstrategy indica si el mtodo de acceso soporta o no un recorrido ordenado. Cero signica que no; si lo hace, amorderstrategy es el nmero de la rutina de estrategia que corresponde al operador de ordenamiento. Por ejemplo, btree tiene amorderstrategy = 1 que corresponde al nmero de estrategia de "menor que". La prxima clase de inters es pg_opclass. Esta clase tiene como nica nalidad asociar un nombre y tipo por defecto con un oid. En pg_amop cada clase de operador B-tree tiene un conjunto de procedimientos, de uno a cinco, descritos ms arriba. Algunas clases de operadores (opclasses) son int2_ops,int4_ops, y oid_ops. Es
89
necesario que Ud. agregue una instancia con su nombre de clase de operador (por ejemplo, complex_abs_ops) a pg_opclass. El oid de esta instancia es una clave fornea en otras clases.
INSERT INTO pg_opclass (opcname, opcdeftype) SELECT complex_abs_ops, oid FROM pg_type WHERE typname = complex_abs SELECT oid, opcname, opcdeftype FROM pg_opclass WHERE opcname = complex_abs_ops; +----+-----------+--------+ |oid | opcname | opcdeftype | +----+-----------+--------+ |17314 | complex_abs_ops | 29058 | +----+-----------+--------+
Ntese que el oid para su instancia de pg_opclass ser diferente! No se preocupe por esto. Obtendremos este nmero del sistema despus igual que acabamos de hacerlo con el oid del tipo aqu. De esta manera ahora tenemos un mtodo de acceso y una clase de operador. An necesitamos un conjunto de operadores; el procedimiento para denir operadores fue discutido antes en este manual. Para la clase de operador complex_abs_ops en Btrees, los operadores que necesitamos son:
valor absoluto valor absoluto or-equal) valor absoluto valor absoluto than-or-equal) valor absoluto menor que (absolute value less-than) menor que o igual a (absolute value less-thanigual (absolute value equal) mayor que o igual a (absolute value greatermayor que (absolute value greater-than)
90
Supongamos que el cdigo que implementa las funciones denidas est almacenado en el archivo PGROOT/src/tutorial/complex.c Parte del cdigo ser parecido a este: (ntese que solamente mostraremos el operador de igualdad en el resto de los ejemplos. Los otros cuatro operadores son muy similares. Rerase a complex.co complex.source para ms detalles.)
#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y) bool complex_abs_eq(Complex *a, Complex *b) { double amag = Mag(a), bmag = Mag(b); return (amag==bmag); }
Hay un par de cosas importantes que suceden arriba. Primero, ntese que se estn deniendo operadores menor que, menor que o igual a, igual, mayor que o igual a, y mayor que para int4. Todos estos operadores ya estn denidos para int4 bajo los nombres <, <=, =, >=, and >. Los nuevos operadores, por supuesto, se comportan de manera distinta. Para garantizar que Postgres usa estos nuevos operadores en vez de los anteriores, es necesario que sean nombrados distinto que ellos. Este es un punto clave: Ud. puede sobrecargar operadores en Postgres, pero slo si el operador no ha sido denido an para los tipos de los argumentos. Es decir, si Ud. tiene < denido para (int4, int4), no puede denirlo nuevamente. Postgres no comprueba esto cuando dene un nuevo operador, as es que debe ser cuidadoso. Para evitar este problema, se utilizarn nombres dispares para los operadores. Si hace esto mal, los mtodos de acceso seguramente fallen cuando intente hacer recorridos. El otro punto importante es que todas las funciones de operador devuelven valores lgicos (Boolean). Los mtodos de acceso cuentan con este hecho. (Por otro lado, las funciones de soporte devuelven cualquier cosa que el mtodo de acceso particular espera en este caso, un entero con signo.) La rutina nal en el archivo es la "rutina de
91
soporte" mencionada cuando tratbamos el atributo amsupport de la clase pg_am. Utilizaremos esto ms adelante. Por ahora, ignrelo.
CREATE FUNCTION complex_abs_eq(complex_abs, complex_abs) RETURNS bool AS PGROOT/tutorial/obj/complex.so LANGUAGE c;
Ahora dena los operadores que los utilizarn. Como se hizo notar, los nombres de operadores deben ser nicos entre todos los operadores que toman dos operandos int4. Para ver si los nombres de operadores listados ms arriba ya han sido ocupados, podemos hacer una consulta sobre pg_operator:
/* * esta consulta utiliza el operador de expresin regular (~) * para encontrar nombres de operadores de tres caracteres que terminen * con el carcter & */ SELECT * FROM pg_operator WHERE oprname ~ ^..&$::text;
para ver si su nombre ya ha sido ocupado para los tipos que Ud. quiere. Las cosas importantes aqu son los procedimientos (que son las funciones Cdenidas ms arriba) y las funciones de restriccin y de selectividad de unin. Ud. debera utilizar solamente las que se usan abajo ntese que hay distintas funciones para los casos menor que, igual, y mayor que. stas deben proporcionarse, o el mtodo de acceso fallar cuando intente utilizar el operador. Debera copiar los nombres para las funciones de restriccin y de unin, pero utilice los nombres de procedimiento que deni en el ltimo paso.
CREATE OPERATOR = (
92
leftarg = complex_abs, rightarg = complex_abs, procedure = complex_abs_eq, restrict = eqsel, join = eqjoinsel )
Tngase en cuenta que se denen cinco operadores correspondientes a menor, menor o igual, igual, mayor, y mayor o igual. Ya casi hemos terminado. La ltima cosa que necesitamos hacer es actualizar la tabla pg_amop. Para hacer esto, necesitamos los siguientes atributos: Tabla 9-3. Esquema de pg_amproc Atributo amopid amopclaid Descripcin el oid de la instancia de pg_am para B-tree (== 403, vase arriba) el oid de la instancia de pg_opclass para complex_abs_ops(== lo que obtuvo en vez de 17314, vase arriba) los oids de los operadores para la clase de operador (opclass) (que obtendremos dentro de un minuto)
amopopr
Entonces necesitamos los oids de los operadores que acabamos de denir. Buscaremos los nombres de todos los operadores que toman dos argumentos de tipo complex, y as sacaremos los nuestros:
SELECT o.oid AS opoid, o.oprname INTO TABLE complex_ops_tmp FROM pg_operator o, pg_type t WHERE o.oprleft = t.oid and o.oprright = t.oid and t.typname = complex_abs;
93
+----+------+ |oid | oprname | +----+------+ |17321 | < | +----+------+ |17322 | <= | +----+------+ |17323 | = | +----+------+ |17324 | >= | +----+------+ |17325 | > | +----+------+
(De nuevo, algunos de sus nmeros de oid sern seguramente diferentes.) Los operadores en los que estamos interesados son los que tienen oids 17321 hasta 17325. Los valores que Ud. obtendr sern probablemente distintos, y debe sustituirlos abajo por estos valores. Haremos esto con una sentencia SELECT. Ahora estamos listos para actualizar pg_amop con nuestra nueva clase de operador. La cosa ms importante en toda esta explicacin es que los operadores estn ordenados desde menor que hasta mayor que, en pg_amop. Agregamos las instancias necesarias:
INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) SELECT am.oid, opcl.oid, c.opoid, 1 FROM pg_am am, pg_opclass opcl, complex_abs_ops_tmp c WHERE amname = btree AND opcname = complex_abs_ops AND c.oprname = <;
Ahora haga lo mismo con los otros operadores sustituyendo el "1" en la tercera lnea de arriba y el "<" en la ltima lnea. Ntese el orden: "menor que" es 1, "menor que o igual a" es 2, "igual" es 3, "mayor que o igual a" es 4, y "mayor que" es 5.
94
El prximo paso es registrar la "rutina de soporte" previamente descrita en la explicacin de pg_am. El oid de esta rutina de soporte est almacenada en la clase pg_amproc, cuya clave est compuesta por el oid del mtodo de acceso y el oid de la clase de operador. Primero, necesitamos registrar la funcin en Postgres (recuerde que pusimos el cdigo C que implementa esta rutina al nal del archivo en el cual implementamos las rutinas del operador):
CREATE FUNCTION complex_abs_cmp(complex, complex) RETURNS int4 AS PGROOT/tutorial/obj/complex.so LANGUAGE c; SELECT oid, proname FROM pg_proc WHERE proname = complex_abs_cmp; +----+-----------+ |oid | proname | +----+-----------+ |17328 | complex_abs_cmp | +----+-----------+
(De nuevo, su nmero de oid ser probablemente distinto y debe sustituirlo abajo por el valor que vea.) Podemos agregar la nueva instancia de la siguiente manera:
INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum) SELECT a.oid, b.oid, c.oid, 1 FROM pg_am a, pg_opclass b, pg_proc c WHERE a.amname = btree AND b.opcname = complex_abs_ops AND c.proname = complex_abs_cmp;
95
Ahora necesitamos agregar una estrategia de hash para permitir que el tipo sea indexado. Hacemos esto utilizando otro tipo en pg_am pero reutilizamos los mismos operadores.
INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) SELECT am.oid, opcl.oid, c.opoid, 1 FROM pg_am am, pg_opclass opcl, complex_abs_ops_tmp c WHERE amname = hash AND opcname = complex_abs_ops AND c.oprname = =;
Para utilizar este ndice en una clusula WHERE, necesitamos modicar la clase pg_operator de la siguiente manera.
UPDATE pg_operator SET oprrest = eqsel::regproc, oprjoin = eqjoinsel WHERE oprname = = AND oprleft = oprright AND oprleft = (SELECT oid FROM pg_type WHERE typname = complex_abs UPDATE pg_operator SET oprrest = neqsel::regproc, oprjoin = neqjoinsel WHERE oprname = AND
oprleft = oprright AND oprleft = (SELECT oid FROM pg_type WHERE typname = complex_abs); UPDATE pg_operator SET oprrest = neqsel::regproc, oprjoin = neqjoinsel WHERE oprname = AND
oprleft = oprright AND oprleft = (SELECT oid FROM pg_type WHERE typname = complex_abs); UPDATE pg_operator SET oprrest = scalarltsel::regproc, oprjoin = scalarltjoinsel WHERE oprname = < AND
96
oprleft = oprright AND oprleft = (SELECT oid FROM pg_type WHERE typname = complex_abs); UPDATE pg_operator SET oprrest = scalarltsel::regproc, oprjoin = scalarltjoinsel WHERE oprname = <= AND oprleft = oprright AND oprleft = (SELECT oid FROM pg_type WHERE typname = complex_abs); UPDATE pg_operator SET oprrest = scalargtsel::regproc, oprjoin = scalargtjoinsel WHERE oprname = > AND oprleft = oprright AND oprleft = (SELECT oid FROM pg_type WHERE typname = complex_abs); UPDATE pg_operator SET oprrest = scalargtsel::regproc, oprjoin = scalargtjoinsel WHERE oprname = >= AND oprleft = oprright AND oprleft = (SELECT oid FROM pg_type WHERE typname = complex_abs);
97
Bueno, no puedo decir que entienda lo que est pasando, pero por lo menos (casi) he logrado portar los ejemplos GiST a linux. El mtodo de acceso GiST ya est en el rbol de postfres (src/backend/access/gist). Examples at Berkeley (ftp://s2k-ftp.cs.berkeley.edu/pub/gist/pggist/pggist.tgz) vienen con una introduccin de los mtodos y demuestran mecanismos de ndices espaciales para cajas 2D, polgonos, intervalos enteros y testo come with an overview of the methods and demonstrate spatial index mechanisms for 2D boxes, polygons, integer intervals and text (vase tambin GiST at Berkeley (http://gist.cs.berkeley.edu:8000/gist/)). En el ejemplo de la caja, se supone que veremos un aumento en el rendimiento al utilizar el ndice GiST; a m me funcion, pero yo no tengo una coleccin razonablemente grande de cajas para comprobar. Otros ejemplos tambin funcionaron, excepto polgonos: obtuve un error al hacer
test=> create index pix on polytmp test-> using gist (p:box gist_poly_ops) with (islossy); ERROR: cannot open pix
98
(PostgreSQL 6.3
Sun Feb
No entiendo el sentido de este mensage de error; parece ser algo que deberamos preguntar a los desarrolladores (mira tambin la Nota 4 ms abajo). Lo que sugerira aqu es que alguien de vosotros, gurs de Linux (linux==gcc?), tomeis las fuentes originales citadas arriba y apliqueis mi parche (vase el adjunto) y nos dijeseis que pensais sobre esto. Me parece muy bien a mi, pero no me gustara mantenerlo mientras que hay tanta gente competente disponible. Unas pocas notas en los fuentes: 1. No fui capaz de utilizar el Makele original (HPUX) y reordenarlo con el viejo tutorial de postgres95 para hacerlo funcionar. Intent mantenerlo genrico, pero no soy un escritor de makeles muy pobre simplemente lo hizo funcionar algn mono. Lo siento, pero creo que ahora es un poco ms portable que el makele original. 2. Compil las fuentes de ejemplo inmediatamente debajo de pgsql/src (simplemente extraje el archivo tar all). El Makele previamente mencionado supone que est un nivel por debajo de pgsql/src (en nuestro caso, en pgsql/src/pggist). 3. Los cambios que hice a los cheros *.c fueron todos sobre #includess, prototipos de funciones y typecasting. Fuera de eso, solamente desech una ristra de variables no utilizadas y aad un par de parentesis para contentar a gcc. Espero que esto no haya enredado las cosas mucho :) 4. Hay un commentario en polyproc.sql:
- - theres a memory leak in rtree poly_ops!! - - create index pix2 on polytmp using rtree (p poly_ops); (- - existe una fuga de memoria en el rtree poly_ops!!) (- - crea un ndice pix2 en polytmp utilizando rtree (p poly_ops)
Pens que podra estar relacionado con un nmero de versin de Postgres anterior e intent la consulta. Mi sistema se volvi loco y tuve que tirar el postmaster en unos diez minutos.
99
Voy a contunuar mirando dentro de GiST un rato, pero tambin agradecera ms ejemplos en la utilizacin de los R-tree.
100
El calicador especial de tipo devuelto OPAQUE le dice a la base de datos que esta funcin no devuelve uno de los tipos denidos en la base de datos ni un tipo compuesto, y que no es directamente utilizable en una sentencia SQL. 3. El PL debe ser declarado con la orden
CREATE [ TRUSTED ] PROCEDURAL LANGUAGE language-name
101
La palabra clave opcional TRUSTED indica si un usuario normal de la base de datos, sin privilegios de superusuario, puede usar este lenguaje para crear funciones y procedimientos activadores. Dado que las funciones de los PL se ejecutan dentro de la aplicacin de base de datos, slo deberan usarse para lenguajes que no puedan conseguir acceso a las aplicaciones internas de la base de datos, o al sistema de cheros. Los lenguajes PL/pgSQL y PL/Tcl son maniestamente ables en este sentido Ejemplo 1. La siguiente orden le dice a la base de datos donde encontrar el objeto compartido para el manejador de funciones que llama al lenguaje PL/pgSQL
CREATE FUNCTION plpgsql_call_handler () RETURNS OPAQUE AS /usr/local/pgsql/lib/plpgsql.so LANGUAGE C;
2.
La orden
CREATE TRUSTED PROCEDURAL LANGUAGE plpgsql HANDLER plpgsql_call_handler LANCOMPILER PL/pgSQL;
dene que la funcin manejadora de llamadas previamente declarada debe ser invocada por las funciones y procedimientos disparadores cuando el atributo del lenguaje es plpgsql Las funciones manejadoras de PL tienen una interfase de llamadas especial distinta del de las funciones de lenguaje C normales. Uno de los argumentos dados al manejador es el identicador del objeto en las entradas de la tabla pg_proc para la funcin que ha de ser ejecutada. El manejador examina varios catlogos de sistema para analizar los argumentos de llamada de la funcin y los tipos de dato que devuelve. El texto fuente del cuerpo de la funcin se encuentra en el atributo prosrc de pg_proc. Debido a esto, en contraste con las funciones de lenguaje C, las
102
funciones PL pueden ser sobrecargadas, como las funciones del lenguaje SQL. Puede haber mltiples funciones PL con el mismo nombre de funcin, siempre que los argumentos de llamada sean distintos. Los lenguajes procedurales denidos en la base de datos template1 se denen automticamente en todas las bases de datos creadas subsecuentemente. As que el administrador de la base de datos puede decidir que lenguajes estn denidos por defecto.
11.2. PL/pgSQL
PL/pgSQL es un lenguaje procedural cargable para el sistema de bases de datos Postgres. Este paquete fue escrito originalmente por Jan Wieck.
11.2.1. Panormica
Los objetivos de diseo de PL/pgSQL fueron crear un lenguaje procedural cargable que
pueda usarse para crear funciones y procedimientos disparados por eventos, aada estructuras de control al lenguaje SQL, pueda realizar clculos complejos, herede todos los tipos denidos por el usuario, las funciones y los operadores, pueda ser denido para ser able para el servidor, sea fcil de usar,
103
El gestor de llamadas PL/pgSQL analiza el texto de las funciones y produce un rbol de instrucciones binarias interno la primera vez que la funcin es invocada por una aplicacin. El bytecode producido es identicado por el manejador de llamadas mediante el ID de la funcin. Esto asegura que el cambio de una funcin por parte de una secuencia DROP/CREATE tendr efecto sin tener que establecer una nueva conexin con la base de datos. Para todas y las expresiones y sentencias SQL usadas en la funcin, el interprete de bytecode de PL/pgSQL crea un plan de ejecucin preparado usando los gestores de SPI, funciones SPI_prepare() y SPI_saveplan(). Esto se hace la primera vez que las sentencias individuales se procesan en la funcin PL/pgSQL. As, una funcin con cdigo condicional que contenga varias sentencias que puedan ser ejecutadas, solo preparar y almacenar las opciones que realmente se usarn durante el mbito de la conexin con la base de datos. Excepto en el caso de funciones de conversin de entrada/salida y de clculo para tipos denidos, cualquier cosa que pueda denirse en funciones de lenguaje C puede ser hecho con PL/pgSQL. Es posible crear funciones complejas de calculo y despus usarlas para denir operadores o usarlas en ndices funcionales.
11.2.2. Descripcion
11.2.2.1. Estructura de PL/pgSQL
El lenguaje PL/pgSQL no es sensible a las maysculas. Todas las palabras clave e identicadores pueden usarse en cualquier mexcla de maysculas y minsculas. PL/pgSQL es un lenguaje orientado a bloques. Un bloque se dene como
[label] [DECLARE declarations] BEGIN statements END;
104
Puede haber cualquier numero de subbloques en la seccin de sentencia de un bloque. Los subloques pueden usarse para ocultar variables a otros bloques de sentencias. Las variables declaradas en la seccin de declaraciones se inicializan a su valor por defecto cada vez que se inicia el bloque, no cada vez que se realiza la llamada a la funcin. Es importante no confundir el signicado de BEGIN/END en la agrupacin de sentencias de OL/pgSQl y las ordenes de la base de datos para control de transacciones. Las funciones y procedimientos disparadores no pueden iniciar o realizar transacciones y Postgres no soporta transacciones anidadas.
11.2.2.2. Comments
Hay dos tipos de comentarios en PL/pgSQl. Un par de guiones comienza un comentario que se extiende hasta el n de la linea. Los caracteres /* comienzan un bloque de comentarios que se extiende hasta que se encuentre un */. Los bloques de comentarios no pueden anidarse pero un par de guiones pueden encerrarse en un bloque de comentario, o ocultar los limitadores de estos bloques.
11.2.2.3. Declaraciones
Todas las variables, las y columnas que se usen en un bloque o subloque ha de ser declarado en la seccin de declaraciones del bloque, excepto las variables de control de bucle en un bucle FOR que se itere en un rango de enteros. Los parmetros dados a una funcin PL/pgSQL se declaran automticamente con los identicadores usuales, $n. Las declaraciones tienen la siguiente sintaxis: name [ CONSTANT ] >typ> [ NOT NULL ] [ DEFAULT | := value ]; Esto declara una variable de un tipo base especicado. Si la variable es declarada como CONSTANT, su valor no podr ser cambiado. Si se especica NOT NULL, la asignacin de un NULL producir un error en timepo de ejecucin. Dado que el valor por defecto de todas las variables es el valor NULL de SQL, todas las variables declaradas como NOT NULL han de tener un valor por defecto.
105
El valor por defecto es evaluado cada vez que se invoca la funcin. As que asignar now a una variable de tipo datetime hace que la variable tome el momento de la llamada a la funcin, no el momento en que la funcin fue compilada a bytecode.
name class%ROWTYPE; Esto declara una la con la estructura de la clase indicada. La clase ha de ser una tabla existente, o la vista de una base de datos. Se accede a los campos de la la mediante la notacin de punto. Los parmetros de una funcin pueden ser de tipos compuestos (las de una tabla completas). En ese caso, el correspondiente identicador $n ser un tipo de la, pero ha de ser referido usando la orden ALIAS que se describe ms adelante. Solo los atributos de usuario de una la de tabla son accesibles en la la, no se puede acceder a Oid o a los otros atributos de sistema (dado que la la puede ser de una vista, y las las de una vista no tienen atributos de sistema tiles). Los campos de un tipo de la heredan los tipos de datos, tamaos y precisiones de las tablas.
name RECORD; Los registros son similares a los tipos de la, pero no tienen una estructura predenida. Se emplean en selecciones y bucles FOR, para mantener una la de la actual base de datos en una operacin SELECT. El mismo registro puede ser usado en diferentes selecciones. El acceso a un campo de registro cuando no hay una la seleccionada resultar en un error de ejecucin. Las las NEW y OLD en un disparador se pasan a los procedimientos como registros. Esto es necesario porque en Postgres un mismo procedimiento desencadenado puede tener sucesos disparadores en diferentes tablas.
106
name ALIAS FOR $n; Para una mejor legibilidad del cdigo, es posible denir un alias para un parmetro posicional de una funcin. Estos alias son necesarios cuando un tipo compuesto se pasa como argumento a una funcin. La notacin punto $1.salary como en funciones SQL no se permiten en PL/pgSQL
RENAME oldname TO newname; Esto cambia el nombre de una variable, registro o la. Esto es til si NEW o OLD ha de ser referenciado por parte de otro nombre dentro de un procedimiento desencadenado.
variable es el nombre de una variable, previamente declarada en la misma funcin, que es visible en este momento. class es el nombre de una tabla existente o vista, donde field es el nombre de un atributo. El uso de class.field %TYPE hace que PL/pgSQl busque las deniciones de atributos en la primera llamada a la funcin, durante toda la vida de la aplicacin nal. Supongamos que tenemos una tabla con un atributo char(20) y algunas funciones
107
PL/pgSQL, que procesan el contenido por medio de variables locales. Ahora, alguien decide que char(20) no es suciente, cierra la tabla, y la recrea con el atributo en cuestin denido como char(40), tras lo que restaura los datos. Pero se ha olvidado de las funciones. Los clculos internos de stas truncarn los valores a 20 caracteres. Pero si hubieran sido denidos usando las declaraciones class.field %TYPE automticamente se adaptarn al cambio de tamao, o a si el nuevo esquema de la tabla dene el atributo como de tipo texto.
11.2.2.5. Expressions
Todas las expresiones en las sentencias PL/pgSQL son procesadas usando backends de ejecucin. Las expresiones que puedan contener constantes pueden de hecho requerir evaluacin en tiempo de ejecucin (por ejemplo, now para el tipo datatime), dado que es imposible para el analizador de PL/pgSQL identicar los valores constantes distintos de la palabra clave NULL. Todas las expresiones se evalan internamente ejecutando una consulta
SELECT expression
usando el gestor SPI. En la expresin, las apariciones de los identicadores de variables son sustituidos por parmetros, y los valores reales de las variables son pasadas al ejecutor en la matriz de parmetros. Todas las expresiones usadas en una funcin PL/pgSQL son preparadas de una sola vez, y guardadas una nica vez. La comprobacin de tipos hecha por el analizador principal de Postgres tiene algunos efectos secundarios en la interpretacin de los valores constantes. En detalle, hay una diferencia entre lo que hacen estas dos funciones
CREATE FUNCTION logfunc1 (text) RETURNS datetime AS DECLARE logtxt ALIAS FOR $1; BEGIN INSERT INTO logtable VALUES (logtxt, now); RETURN now;
108
y
CREATE FUNCTION logfunc2 (text) RETURNS datetime AS DECLARE logtxt ALIAS FOR $1; curtime datetime; BEGIN curtime := now; INSERT INTO logtable VALUES (logtxt, curtime); RETURN curtime; END; LANGUAGE plpgsql;
En el caso de logfunc1(), el analizador principal de Postgres sabe cuando prepara la ejecucin de INSERT que la cadena now debe ser interpretada como una fecha, dado que el campo objeto de logtable tiene ese tipo. As, har una constante de ese tipo, y el valor de esa constante se emplear en todas las llamadas a logfunc1(), durante toda la vida til de ese proceso. No hay que decir que eso no era lo que pretenda el programador. En el caso de logfunc2(), el analizador principal de Postgres no sabe cual es el tipo de now, por lo que devuelve un tipo de texto, que contiene la cadena now. Durante la asignacin a la variable local curtime, el interprete PL/pgSQL asigna a esta cadena el tipo fecha, llamando a las funciones text_out() y datetime_in() para realizar la conversin. esta comprobacin de tipos realizada por el analizador principal de Postgres fue implementado antes de que PL/pgSQL estuviera totalmente terminado. Es una diferencia entre 6.3 y 6.4, y afecta a todas las funciones que usan la planicacin realizada por el gestor SPI. El uso de variables locales en la manera descrita anteriormente es actualmente la nica forma de que PL/pgSQL interprete esos valores correctamente.
109
Si los campos del registro son usados en expresiones o sentencias, los tipos de datos de campos no deben cambiarse entre llamadas de una misma expresin. Tenga esto en cuenta cuando escriba procedimientos disparadores que gestionen eventos en ms de una tabla.
11.2.2.6. Sentencias
Cualquier cosa no comprendida por el analizador PL/pgSQL tal como se ha especicado ser enviado al gestor de bases de datos, para su ejecucin. La consulta resultante no devolver ningn dato. Asignacin Una asignacin de un valor a una variable o campo de la o de registro se escribe:
identifier := expression;
Si el tipo de dato resultante de la expresin no coincide con el tipo de dato de las variables, o la variable tienen un tamao o precisin conocido (como char(29)), el resultado ser amoldado implcitamente por el interprete de bytecode de PL/pgSQL, usando los tipos de las variables para las funciones de entrada y los tipos resultantes en las funciones de salida. Ntese que esto puede potencialmente producir errores de ejecucin generados por los tipos de las funciones de entrada. Una asignacin de una seleccin completa en un registro o la puede hacerse del siguiente modo:
SELECT expressions INTO target FROM ...;
target puede ser un registro, una variable de la o una lista separada por comas de variables y campo de de registros o las. Si una la o una lista de variables se usa como objetivo, los valores seleccionados han de coincidir exactamente con la estructura de los objetivos o se producir un error de ejecucin. La palabra clave FROM puede preceder a cualquier calicador vlido, agrupacin, ordenacin, etc. que pueda pasarse a una sentencia SELECT.
110
Existe una variable especial llamada FOUND de tipo booleano, que puede usarse inmediatamente despus de SELECT INTO para comprobar si una asignacin ha tenido xito.
SELECT * INTO myrec FROM EMP WHERE empname = myname; IF NOT FOUND THEN RAISE EXCEPTION employee % not found, myname; END IF;
Si la seleccin devuelve mltiples las, solo la primera se mueve a los campos objetivo. todas las dems se descartan.
Llamadas a otra funcin Todas las funciones denidas en una base de datos Postgres devuelven un valor. Por lo tanto, la forma normal de llamar a una funcin es ejecutar una consulta SELECT o realizar una asignacin (que de lugar a un SELECT interno de PL/pgSQL). Pero hay casos en que no interesa saber los resultados de las funciones.
PERFORM query
Esto ejecuta SELECT query en el gestor SPI, y descarta el resultado. Los identicadores como variables locales son de todos modos sustituidos en los parmetros. Volviendo de la funcin
RETURN expression
La funcin termina y el valor de expression se devolver al ejecutor superior. El valor devuelto por una funcin no puede quedar son denir. Si el control alcanza el n del bloque de mayor nivel de la funcin sin encontrar una sentencia RETURN, ocurrir un error de ejecucin.
111
Las expresiones resultantes sern amoldadas automticamente en los tipos devueltos por la funcin, tal como se ha descrito en el caso de las asignaciones.
Abortando la ejecucin y mensajes Como se ha indicado en los ejemplos anteriores, hay una sentencia RAISE que puede enviar mensajes al sistema de registro de Postgres. ###################### ATENCION WARNING ACHTUNG ##################### Aqu puede haber una errata! Comparad con el original
RAISE level for [, identifier [...]];
##################################################################### Dentro del formato, % se usa como situacin para los subsecuentes identicadores, separados por comas. Los posibles niveles son DEBUG (suprimido en las bases de datos de produccin), NOTICE (escribe en el registro de la base de datos y lo enva a la aplicacin del cliente) y EXCEPTION (escribe en el registro de la base de datos y aborta la transaccin). Condiciones
expression debe devolver un valor que al menos pueda ser adaptado en un tipo booleano.
112
Se trata de un bucle no condicional que ha de ser terminado de forma explicita, mediante una sentencia EXIT. La etiqueta opcional puede ser usado por las sentencias EXIT de otros bucles anidados, para especicar el nivel del bucle que ha de terminarse.
[label] WHILE expression LOOP statements END LOOP;
Se trata de un lazo condicional que se ejecuta mientras la evaluacin de expression sea cierta.
[label] FOR name IN [ REVERSE ] express .. expression LOOP statements END LOOP;
Se trata de un bucle que se itera sobre un rango de valores enteros. La variable name se crea automticamente con el tipo entero, y existe solo dentro del bucle. Las dos expresiones dan el limite inferior y superior del rango y son evaluados slo cuando se entra en el bucle. El paso de la iteracin es siempre 1.
[label] FOR record | row IN select_clause LOOP statements END LOOP;
113
EL registro o la se asigna a todas las las resultantes de la clausula de seleccin, y la sentencia se ejecuta para cada una de ellas. Si el bucle se termina con una sentencia EXIT, la ultima la asignada es an accesible despus del bucle.
EXIT [ label ] [ WHEN expression ];
Si no se incluye label, se termina el lazo ms interno, y se ejecuta la sentencia que sigue a END LOOP. Si se incluye label ha de ser la etiqueta del bucle actual u de otro de mayor nivel. EL bucle indicado se termina, y el control se pasa a la sentencia de despus del END del bucle o bloque correspondiente.
114
TG_WHEN Tipo de dato texto; es una cadena de caracteres del tipo BEFORE o AFTER, dependiendo de la denicin del procedimiento desencadenado. TG_LEVEL Tipo de dato texto; una cadena de ROW o STATEMENT, dependiendo de la denicin del procedimiento desencadenado. TG_OP Tipo de dato texto; una cadena de INSERT, UPDATE o DELETE, que nos dice la operacin para la que se ha disparado el procedimiento desencadenado. TG_RELID Tipo de dato oid; el ID del objeto de la tabla que ha provocado la invocacin del procedimiento desencadenado. TG_RELNAME Tipo de dato nombre; el nombre de la tabla que ha provocado la activacin del procedimiento desencadenado. TG_NARGS Tipo de dato entero; el numero de argumentos dado al procedimiento desencadenado en la sentencia CREATE TRIGGER. TG_ARGV[] Tipo de dato matriz de texto; los argumentos de la sentencia CREATE TRIGGER. El ndice comienza por cero, y puede ser dado en forma de expresin. ndices no validos dan lugar a un valor NULL. En segundo lugar, han de devolver o NULL o una la o registro que contenga exactamente la estructura de la tabla que ha provocado la activacin del procedimiento desencadenado. Los procedimientos desencadenados activados por AFTER deben devolver siempre un valor NULL, sin producir ningn efecto. Los procedimientos
115
desencadenados activados por BEFORE indican al gestor de procedimientos desencadenados que no realice la operacin sobre la la actual cuando se devuelva NULL. En cualquier otro caso, la la o registro devuelta sustituye a la la insertada o actualizada. Es posible reemplazar valores individuales directamente en una sentencia NEW y devolverlos, o construir una nueva la o registro y devolverla.
11.2.2.8. Excepciones
Postgres no dispone de un modelo de manejo de excepciones muy elaborado. Cuando el analizador, el optimizador o el ejecutor deciden que una sentencia no puede ser procesada, la transaccin completa es abortada y el sistema vuelve al lazo principal para procesar la siguiente consulta de la aplicacin cliente. Es posible introducirse en el mecanismo de errores para detectar cuando sucede esto. Pero lo que no es posible es saber qu ha causado en realidad el aborto (un error de conversin de entrada/salida, un error de punto otante, un error de anlisis). Y es posible que la base de datos haya quedado en un estado inconsistente, por lo que volver a un nivel de ejecucin superior o continuar ejecutando comandos puede corromper toda la base de datos. E incluso aunque se pudiera enviar la informacin a la aplicacin cliente, la transaccin ya se abra abortado, por lo que carecera de sentido el intentar reanudar la operacin. Por todo esto, lo nico que hace PL/pgSQL cuando se produce un aborto de ejecucin durante la ejecucin de una funcin o procedimiento disparador es enviar mensajes de depuracin al nivel DEBUG, indicando en qu funcin y donde (numero de lnea y tipo de sentencia) ha sucedido el error.
11.2.3. Ejemplos
Se incluyen unas pocas funciones para demostrar lo fcil que es escribir funciones en PL/pgSQL. Para ejemplos ms complejos, el programador debera consultar el test de regresin de PL/pgSQL.
116
Un detalle doloroso a la hora de escribir funciones en PL/pgSQL es el manejo de la comilla simple. El texto de las funciones en CREATE FUNCTION ha de ser una cadena de texto. Las comillas simples en el interior de una cadena literal deben de duplicarse o anteponerse de una barra invertida. An estamos trabajando en una alternativa ms elegante. Mientras tanto, duplique las comillas sencillas como en los ejemplos siguientes. Cualquier solucin a este problema en futuras versiones de Postgres mantendrn la compatibilidad con esto.
CREATE FUNCTION concat_text (text, text) RETURNS text AS BEGIN RETURN $1 || $2; END; LANGUAGE plpgsql;
117
emprec ALIAS FOR $1; sallim ALIAS FOR $2; BEGIN IF emprec.salary ISNULL THEN RETURN f; END IF; RETURN emprec.salary > sallim; END; LANGUAGE plpgsql;
118
IF NEW.salary < 0 THEN RAISE EXCEPTION % cannot have a negative salary, NEW.empna END IF; - Remember who changed the payroll when NEW.last_date := now; NEW.last_user := getpgusername(); RETURN NEW; END; LANGUAGE plpgsql; CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
11.3. PL/Tcl
PL/Tcl es un lenguaje procedural para el gestor de bases de datos Postgres que permite el uso de Tcl para la creacin de funciones y procedimientos desencadenados por eventos. Este paquete fue escrito originalmente por Jan Wieck.
11.3.1. Introduccin
PL/Tcl ofrece la mayora de las capacidades de que dispone el lenguaje C, excepto algunas restricciones. Las restricciones buenas son que todo se ejecuta en un buen interprete Tcl. Adems del reducido juego de ordenes de Tcl, solo se disponen de unas pocas ordenes para acceder a bases de datos a travs de SPI y para enviar mensajes mediante elog(). No hay forma
119
de acceder a las interioridades del proceso de gestin de la base de datos, no de obtener acceso al nivel del sistema operativo, bajo los permisos del identicador de usuario de Postgres, como es posible en C. As, cualquier usuario de bases de datos sin privilegios puede usar este lenguaje. La otra restriccin, interna, es que los procedimientos Tcl no pueden usarse para crear funciones de entrada / salida para nuevos tipos de datos. Los objetos compartidos para el gestor de llamada PL/Tcl se construyen automticamente y se instalan en el directorio de bibliotecas de Postgres, si el soporte de Tcl/Tk ha sido especicado durante la conguracin, en el procedimiento de instalacin.
11.3.2. Descripcin
11.3.2.1. Funciones de Postgres y nombres de procedimientos Tcl
En Postgres, un mismo nombre de funcin puede usarse para diferentes funciones, siempre que el numero de argumentos o sus tipos sean distintos. Esto puede ocasionar conictos con los nombres de procedimientos Tcl. Para ofrecer la misma exibilidad en PL/Tcl, los nombres de procedimientos Tcl internos contienen el identicador de objeto de la la de procedimientos pg_proc como parte de sus nombres. As, diferentes versiones (por el numero de argumentos) de una misma funcin de Postgres pueden ser diferentes tambin para Tcl.
120
Cuando se invoca esta funcin en una consulta, los argumentos se dan como variables $1 ... $n en el cuerpo del procedimiento Tcl. As, una funcin de mximo que devuelva el mayor de dos valores int4 sera creada del siguiente modo:
CREATE FUNCTION tcl_max (int4, int4) RETURNS int4 AS if {$1 > $2} {return $1} return $2 LANGUAGE pltcl;
Argumentos de tipo compuesto se pasan al procedimiento como matrices de Tcl. Los nombres de elementos en la matriz son los nombres de los atributos del tipo compuesto. Si un atributo de la la actual tiene el valor NULL, no aparecer en la matriz! He aqu un ejemplo que dene la funcin overpaid_2 (que se encuentra en la antigua documentacin de Postgres), escrita en PL/Tcl
CREATE FUNCTION overpaid_2 (EMP) RETURNS bool AS if {200000.0 < $1(salary)} { return "t" } if {$1(age) < 30 && 100000.0 < $1(salary)} { return "t" } return "f" LANGUAGE pltcl;
121
122
desencadenante. $TG_level La cadena ROW o STATEMENT, dependiendo del suceso de la llamada desencadenante. $TG_op La cadena INSERT, UPDATE o DELETE, dependiendo del suceso de la llamada desencadenante. $NEW Una matriz que contiene los valores de la la de la nueva tabla para acciones INSERT/UPDATE, o vaca para DELETE. $OLD Una matriz que contiene los valores de la la de la vieja tabla para acciones UPDATE o DELETE, o vaca para INSERT. $GD La matriz de datos de estado global, como se describa ms adelante. $args Una lista Tcl de los argumentos del procedimiento como se dan en la sentencia CREATE TRIGGER. Los argumentos son tambin accesibles como $1 ... $n en el cuerpo del procedimiento. EL valor devuelto por un procedimiento desencadenado es una de las cadenas OK o SKIP, o una lista devuelta por la orden Tcl array get. Si el valor devuelto es OK, la operacin normal que ha desencadenado el procedimiento (INSERT/UPDATE/DELETE) tendr lugar. Obviamente, SKIP le dice al gestor de procesos desencadenados que suprima silenciosamente la operacin. La lista de array get le dice a PL/Tcl que devuelva una la modicada al gestor de procedimientos desencadenados que ser insertada en lugar de la dada en $NEW (solo para
123
INSERT/UPDATE). No hay que decir que todo esto solo tiene sentido cuando el desencadenante es BEFORE y FOR EACH ROW. Ha aqu un pequeo ejemplo de procedimiento desencadenado que fuerza a un valor entero de una tabla a seguir la pista del numero de actualizaciones que se han realizado en esa la. Para cada nueva la insertada, el valor es inicializado a 0, e incrementada en cada operacin de actualizacin:
CREATE FUNCTION trigfunc_modcount() RETURNS OPAQUE AS switch $TG_op { INSERT { set NEW($1) 0 } UPDATE { set NEW($1) $OLD($1) incr NEW($1) } default { return OK } } return [array get NEW] LANGUAGE pltcl; CREATE TABLE mytab (num int4, modcnt int4, desc text); CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab FOR EACH ROW EXECUTE PROCEDURE trigfunc_modcount(modcnt);
124
elog level msg Lanza un mensaje de registro. Los posibles niveles son NOTICE, WARN, ERROR, FATAL, DEBUG y NOIND, como en la funcin elog() de C. quote string Duplica todas las apariciones de una comilla o de la barra invertida. Debera usarse cuando las variables se usen en la cadena en la cadena de la consulta enviada a spi_exec o spi_prepara (no en la lista de valores de spi_execp). Consideremos una cadena de consulta como esta:
"SELECT $val AS ret"
Donde la variable Tcl val contiene "doesnt". Esto da lugar a la cadena de consulta
"SELECT doesnt AS ret"
que produce un error del analizador durante la ejecucin de spi_exec o spi_prepare. Debera contener
"SELECT doesnt AS ret"
spi_exec ?-count n? ?-array nam?que ?loop-body ? Llama al analizador/planicador/optimizador/ejecutos de la consulta. El valor opcional -count la dice a spi_exec el mximo numero de las que han de ser procesadas por la consulta. Si la consulta es una sentencia SELECT y se incluye el cuerpo del lazo opcional (un cuerpo de sentencias Tcl similar a una sentencia anticipada), se evala para cada la seleccionada, y se comporta como se espera, tras continua/break. Los
125
valores de los campos seleccionados se colocan en nombres de variables, como nombres de columnas. As,
spi_exec "SELECT count(*) AS cnt FROM pg_proc"
pondr en la variable cnt el numero de las en el catlogo de sistema pg_proc. Si se incluye la opcin -array, los valores de columna son almacenados en la matriz asociativa llamada name, indexada por el nombre de la columna, en lugar de en variables individuales.
spi_exec -array C "SELECT * FROM pg_class" { elog DEBUG "have table $C(relname)" }
imprimir un mensaje de registro DEBUG para cada una de las las de pg_class. El valor devuelto por spi_exec es el numero de las afectado por la consulta, y se encuentra en la variable global SPI_processed.
spi_prepare query typelist Prepara Y GUARDA una consulta para una ejecucin posterior. Es un poco distinto del caso de C, ya que en ese caso, la consulta prevista es automticamente copiada en el contexto de memoria de mayor nivel. Por lo tanto, no actualmente ninguna forma de planicar una consulta sin guardarla. Si la consulta hace referencia a argumentos, los nombres de los tipos han de incluirse, en forma de lista Tcl. El valor devuelto por spi_prepare es el identicador de la consulta que se usar en las siguientes llamadas a spi_execp. Vase spi_execp para un ejemplo.
spi_exec ?-count n? ?-array nam? ?-nullsesquvalue? ?loop-body ? Ejecuta una consulta preparada en spi_prepare con sustitucin de variables. El valor opcional -count le dice a spi_execp el mximo numero de las que se
126
procesarn en la consulta. El valor opcional para -nulls es una cadena de espacios de longitud "n", que le indica a spi_execp qu valores son NULL. Si se indica, debe tener exactamente la longitud del numero de valores. El identicador de la consulta es el identicador devuelto por la llamada a spi_prepare. Si se pasa una lista de tipos a spi_prepare, ha de pasarse una lista Tcl de exactamente la misma longitud a spi_execp despus de la consulta. Si la lista de tipos de spi_prepare est vaca, este argumento puede omitirse. Si la consulta es una sentencia SELECT, lo que se ha descrito para spi_exec ocurrir para el cuerpo del bucle y las variables de los campos seleccionados. He aqu un ejemplo de una funcin PL/Tcl que usa una consulta planicada:
CREATE FUNCTION t1_count(int4, int4) RETURNS int4 AS if {![ info exists GD(plan) ]} { # prepare the saved plan on the first call set GD(plan) [ spi_prepare \\ "SELECT count(*) AS cnt FROM t1 WHERE num >= \\$1 AND int4 ] } spi_execp -count 1 $GD(plan) [ list $1 $2 ] return $cnt LANGUAGE pltcl;
Ntese que cada una de las barras invertidas que Tcl debe ver ha de ser doblada en la consulta que crea la funcin, dado que el analizador principal procesa estas barras en CREATE FUNCTION. Dentro de la cadena de la consulta que se pasa a spi_prepare debe haber un signo $ para marcar la posicin del parmetro, y evitar que $1 sea sustituido por el valor dado en la primera llamada a la funcin.
127
Mdulos y la orden desconocido PL/Tcl tiene una caracterstica especial para cosas que suceden raramente. Reconoce dos tablas "mgicas", pltcl_modules y pltcl_modfuncs. Si existen, el mdulo desconocido es cargado por el interprete, inmediatamente tras su creacin. Cada vez que se invoca un procedimiento Tcl desconocido, el procedimiento desconocido es comprobado, por si el procedimiento en cuestin est denido en uno de esos mdulos. Si ocurre esto, el mdulo es cargado cuando sea necesario. Para habilitar este comportamiento, el gestor de llamadas de PL/Tcl ha de ser compilado con la opcin -DPLTCL_UNKNOWN_SUPPORT habilitado. Existen scripts de soporte para mantener esas tablas en el subdirectorio de mdulos del cdigo fuente de PL/Tcl, incluyendo el cdigo fuente del mdulo desconocido, que ha de ser instalado inicialmente.
128
Carga dinmica (Dynamic loading) el lo que Postgres hace con un chero objeto. El chero objeto se copia en el servidor Postgres en ejecucin, y las funciones y variables del chero quedan disponibles para las funciones de los procesos Postgres. Postgres hace sto usando el mecanismo de carga dinmica proporcionado por el sistema operativo. Conguracion de la carga y enlazado (Loading and link editing) es lo que usted hace con un chero objeto a n de producir otro tipo de chero objeto (por ejemplo, un programa ejecutable o una biblioteca compartida). Esto se realiza por medio del programa de conguracin de enlazado, ld(1).
129
Las rutas dadas a la orden para crear la funcin deben ser absolutas (es decir, han de empezar con "/"), y referirse a directorios visibles para la mquina en la que se est ejecutando el servidor Postgres.
Sugerencia: Las rutas relativas tambien funcionan, pero hay que terner en cuenta que seran relativas al directorio donde reside la base de datos (que es generalmente invisible para las aplicaciones nales). Obviamente, no tiene sentido hacer la ruta relativa al directorio en el que el usuario inicial la aplicacion nal, dado que el servidor puede estar ejecutndose en una mquina distinta.
El usuario Postgres debe ser capaz de recorrer la ruta dada a la orden de creacin de la funcin, y ser capaz de leer el chero objeto. Esto es as porque el servidor Postgres se ejecuta como usuario Postgres, no como el usuario que inicia el proceso nal. (Hacer el chero el el directorio de nivel superior no leible y/o no ejecutable para el usuario "postgres" es un error extremadamente comn.) Los nombre de simbolos denidos en los chero objetos no deben estar en conicto entre s, ni con los simbolos denidos en Postgres . El compilador de C GNU normalmente no dispone de las opciones especiales necesarias para usar la interfase del cargador dinmico del systema. En caso de que esto ocurra, ha de usarse el compilador de C que venga con el sistema operativo.
12.1. ULTRIX
Es muy facil escribir cheros objeto de carga dinmica bajo ULTRIX. ULTRIX no tiene ningn mecanismo para bibliotecas compartidas, y por lo tanto, no plantea restricciones a la interfase del cargador dinmico. Por otra parte, tendremos que (re)escribir un cargador dinmico no portable, y no podremos usar verdaderas bibliotecas compartidas. Bajo ULTRIX, la unica restriccion es que debe producir cada
130
chero objeto con la opcion -G 0. (Ntese que es trata del nmero 0, no del literal "o"). Por ejemplo:
# simple ULTRIX example % cc -G 0 -c foo.c
produce un chero objeto llamado foo.o que puede ser cargado dinmicamente en Postgres . No ha de realizarse carga o enlazado adicional.
El objeto compartido resultante puede entonces ser cargado en Postgres. Cuando especique el nombre del chero objeto para la orden de creacin, ha de dar el nombre del chero objeto compartido (termiando en .so) en lugar de el del chero objeto normal.
Sugerencia: En realidad, Postgres. no se preocupa del nombre del chero, mientras sea un chero objeto compartido. Si preere denominar el nombre del chero compartido con la extensin .o, esto estar bien para Postgres, siempre que se asegura de que se envia el nombre correcto al comando de creacin. En otras palabras, ha de ser consistente. Sin embargo, desde un punto de vista prctico, no recomendamos esta prctica, dado que puede acabar confundindole respecto a que cheros han sido convertidos en objetos compartidos, y que cheros no. Por ejmplo, es muy dicil escribir Makeles para
131
realizar un enlace automtico, si tanto los cheros objeto, como los objetos compartidos tienen la extensin .o
Como los cheros .so mencionados en la anterior subseccin, hay que indicarle a la orden de creacin de funciones que chero es el que hay que cargar (por ejemplo, puede darle la localizacin de la biblioteca compartida, o chero .sl). Bajo SunOS 4.x es algo as:
# simple SunOS 4.x example % cc -PIC -c foo.c % ld -dc -dp -Bdynamic -o foo.so foo.o
132
o
# simple Solaris 2.x example % gcc -fPIC -c foo.c % ld -G -Bdynamic -o foo.so foo.o
Cuando enlace bibliotecas compartidas, puede tener que especicar bibliotecas compartidas adicionales (normalemente bibliotecas de sistema, como las bibliotecas de C y matemticas) en la linea de ordenes de ld
133
El nombre del trigger se usar si se desea eliminar el trigger. Se usa como argumento del comando DROP TRIGGER. La palabra siguiente determina si la funcin debe ser llamada antes (BEFORE) o despus (AFTER) del evento. El siguiente elemento del comando determina en que evento/s ser llamada la funcin. Es posible especicar mltiples eventos utilizado el operador OR. El nombre de la relacin (relation name) determinar la tabla afectada por el evento.
134
La instruccin FOR EACH determina si el trigger se ejecutar para cada la afectada o bien antes (o despus) de que la secuencia se haya completado. El nombre del procedimiento (procedure name) es la funcin C llamada. Los argumentos son pasados a la funcin en la estructura CurrentTriggerData. El propsito de pasar los argumentos a la funcin es permitir a triggers diferentes con requisitos similares llamar a la misma funcin. Adems, la funcin puede ser utilizada para disparar distintas relaciones (estas funciones son llamadas "general trigger funcions"). Como ejemplo de utilizacin de lo descrito, se puede hacer una funcin general que toma como argumentos dos nombres de campo e inserta el nombre del usuario y la fecha (timestamp) actuales en ellos. Esto permite, por ejemplo, utilizar los triggers en los eventos INSERT para realizar un seguimiento automtico de la creacin de registros en una tabla de transacciones. Se podra utilizar tambin para registrar actualizaciones si es utilizado en un evento UPDATE. Las funciones trigger retornan un rea de tuplas (HeapTuple) al ejecutor. Esto es ignorado para trigger lanzados tras (AFTER) una operacin INSERT, DELETE o UPDATE, pero permite lo siguiente a los triggers BEFORE: - retornar NULL e ignorar la operacin para la tupla actual (y de este modo la tupla no ser insertada/actualizada/borrada); - devolver un puntero a otra tupla (solo en eventos INSERT y UPDATE) que sern insertados (como la nueva versin de la tupla actualizada en caso de UPDATE) en lugar de la tupla original. Notar que no hay inicializacin por parte del CREATE TRIGGER handler. Esto ser cambiado en el futuro. Adems, si ms de un trigger es denido para el mismo evento en la misma relacin, el orden de ejecucin de los triggers es impredecible. Esto puede ser cambiado en el futuro. Si una funcin trigger ejecuta consultas SQL (utilizando SPI) entonces estas funciones pueden disparar nuevos triggers. Esto es conocido como triggers en cascada. No hay ninguna limitacin explicita en cuanto al nmero de niveles de cascada. Si un trigger es lanzado por un INSERT e inserta una nueva tupla en la misma relacin, el trigger ser llamado de nuevo (por el nuevo INSERT). Actualmente, no se proporciona ningn mecanismo de sincronizacin (etc) para estos casos pero esto puede
135
cambiar. Por el momento, existe una funcin llamada funny_dup17() en los tests de regresin que utiliza algunas tcnicas para parar la recursividad (cascada) en si misma...
TRIGGER_FIRED_BEFORE(event) devuelve TRUE si el trigger se dispar antes; TRIGGER_FIRED_AFTER(event) devuelve TRUE si se dispar despus; TRIGGER_FIRED_FOR_ROW(event) devuelve TRUE si el trigger se dispar para un evento a nivel de fila; TRIGGER_FIRED_FOR_STATEMENT(event) devuelve TRUE si el trigger se dispar para un evento a nivel de sentencia. TRIGGER_FIRED_BY_INSERT(event) devuelve TRUE si fue disparado por un INSE
136
TRIGGER_FIRED_BY_DELETE(event) devuelve TRUE si fue disparado por un DELE TRIGGER_FIRED_BY_UPDATE(event) devuelve TRUE si fue disparado por un UPDA
tg_relation es un puntero a una estructura que describe la relacin disparadora. Mirar en src/include/utils/rel.h para ver detalles sobre esta estructura. Lo ms interesante es tg_relation->rd_att (descriptor de las tuplas de la relaci y tg_relation->rd_rel->relname (nombre de la relacin. No es un char*, si NameData. Utilizar SPI_getrelname(tg_relation) para obtener char* si se necesita una copia del nombre). tg_trigtuple es un puntero a la tupla por la que to es, la tupla que se est insertando (en un actualizando (UPDATE). En caso de un INSERT/DELETE esto es cutor si no se desea reemplazar la tupla con
tg_newtuple es un puntero a la nueva tupla en caso de UPDATE y NULL si es para un INSERT o un DELETE. Esto es lo que debe devolverse al Ejecutor en el caso de un UPDATE si no se desea reemplazar la tupla por otra o ignorar la operacin
tg_trigger es un puntero a la estructura Trigger definida en src/include/utils/rel.h typedef struct Trigger { Oid tgoid; char *tgname; Oid tgfoid;
137
FmgrInfo int16 bool bool bool bool int16 int16 char } Trigger;
tgname es el nombre del trigger, tgnargs es el nmero de argumentos en tgargs, tgargs es un array de punteros a los argumentos especificados en el CREAT TRIGGER. Otros miembros son exclusivamente para uso interno.
las tuplas insertadas son invisibles para el propio SELECT. En efecto, esto duplica la tabla dentro de s misma (sujeto a las reglas de ndice nico, por supuesto) sin recursividad. Pero hay que recordar esto sobre visibilidad en la documentacin de SPI:
Los cambios hechos por la consulta Q son visibles por las consultas que empiezan tras la consulta Q, no importa si son iniciados desde Q (durante
138
Esto es vlido tambin para los triggers, as mientras se inserta una tupla (tg_trigtuple) no es visible a las consultas en un trigger BEFORE, mientras que esta tupla (recin insertada) es visible a las consultas de un trigger AFTER, y para las consultas en triggers BEFORE/AFTER lanzados con posterioridad!
13.4. Ejemplos
Hay ejemplos ms complejos en src/test/regress/regress.c y en contrig/spi. He aqu un ejemplo muy sencillo sobre el uso de triggers. La funcin trigf devuelve el nmero de tuplas en la relacin ttest e ignora la operacin si la consulta intenta insertar NULL en x (i.e - acta como una restriccin NOT NULL pero no aborta la transaccin).
#include "executor/spi.h" /* Necesario para trabajar con SPI */ #include "commands/trigger.h" /* -"- y triggers */ HeapTuple trigf(void); HeapTuple trigf() { TupleDesc tupdesc; HeapTuple rettuple; char *when; bool checknull = false; bool isnull; int ret, i; if (!CurrentTriggerData) elog(WARN, "trigf: triggers sin inicializar"); /* tupla para devolver al Ejecutor */
139
if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event)) rettuple = CurrentTriggerData->tg_newtuple; else rettuple = CurrentTriggerData->tg_trigtuple; /* comprobar NULLs ? */ if (!TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event) && TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event)) checknull = true; if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event)) when = "antes "; else when = "despus "; tupdesc = CurrentTriggerData->tg_relation->rd_att; CurrentTriggerData = NULL; /* Conexin al gestor SPI */ if ((ret = SPI_connect()) < 0) elog(WARN, "trigf (lanzado %s): SPI_connect devolvi %d", when, ret); /* Obtiene el nmero de tuplas en la relacin */ ret = SPI_exec("select count(*) from ttest", 0); if (ret < 0) elog(WARN, "trigf (lanzado %s): SPI_exec devolvi %d", when, ret);
i = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull); elog (NOTICE, "trigf (lanzado %s): hay %d tuplas en ttest", when, i); SPI_finish(); if (checknull) { i = SPI_getbinval(rettuple, tupdesc, 1, &isnull);
140
Ahora, compila y create table ttest (x int4); create function trigf () returns opaque as ...path_to_so language c;
vac=> create for each row CREATE vac=> create for each row CREATE vac=> insert NOTICE:trigf INSERT 0 0 trigger tbefore before insert or update or delete on ttest execute procedure trigf(); trigger tafter after insert or update or delete on ttest execute procedure trigf(); into ttest values (null); (fired before): there are 0 tuples in ttest
- Insertion skipped and AFTER trigger is not fired vac=> select * from ttest; x (0 rows) vac=> insert into ttest values (1); NOTICE:trigf (fired before): there are 0 tuples in ttest NOTICE:trigf (fired after ): there are 1 tuples in ttest ^^^^^^^^ remember what we said about visibility. INSERT 167793 1 vac=> select * from ttest; x -
141
1 (1 row) vac=> insert into ttest select x * 2 from ttest; NOTICE:trigf (fired before): there are 1 tuples in ttest NOTICE:trigf (fired after ): there are 2 tuples in ttest ^^^^^^^^ remember what we said about visibility. INSERT 167794 1 vac=> select * from ttest; x 1 2 (2 rows) vac=> update NOTICE:trigf UPDATE 0 vac=> update NOTICE:trigf NOTICE:trigf UPDATE 1 vac=> select x 1 4 (2 rows) vac=> delete NOTICE:trigf NOTICE:trigf NOTICE:trigf NOTICE:trigf ttest set x = null where x = 2; (fired before): there are 2 tuples in ttest ttest set x = 4 where x = 2; (fired before): there are 2 tuples in ttest (fired after ): there are 2 tuples in ttest * from ttest;
from ttest; (fired before): (fired after ): (fired before): (fired after ):
142
Nota: Aportacin del traductor.Manuel Martnez Valls En la version 6.4 ya existian los triggers, lo que eran triggers para tuplos, (FOR EACH ROW) pero no para sentencias (FOR STATEMENT), por eso creo que es importante poner disparadores para sentencias, no disparadores solo. Los trigger son parte de lo que se conoce como "elementos activos" de una BD. Asi como lo son las constraints tales como NOT NULL, FOREIGN KEY, PRIMARY KEY, CHECK. Una vez denidas ellas "se activaran" solo al ocurrir un evento que las viole, un valor nulo en un campo con NOT NULL, etc Por que entonces llamar triggers a los triggers? ;Con ellos se quizo dar mas control al programador sobre los eventos que desencadenan un elemento activo, se le conoce en ingles como ECA rules o event-condition-action rule. Es por ello que los triggers tienen una clausula BEFORE, AFTER o INSTEAD (por cierto pgsql no tiene INSTEAD) y bajo que evento (INSERT, UPDATE, DELETE) pero de esta forma el trigger se ejecutara para tuplo (o la) sometido al evento (clausula FOR EACH ROW) pero el standard (que pgsql no cubre completamente) dice que puede ser tambien FOR EACH SENTENCE. Esto provoca que se ejecute el trigger para toda la relacion (o tabla) para la cual se dene (clausula ON). La diferencia para los que lo han programado, por ejemplo en plpgsql, queda clara entonces: cuando es FOR EACH ROW en la funcion pgsql que implementa el trigger se tiene un objeto NEW y uno OLD que se reere a la tupla completa, en el trigger de STATEMENT tiene un objeto NEW y OLD que son la relacion (o tabla) completa Esta claro entonces que es un poco mas dicil implementar un trigger para statement que para la (todavia pgsql no lo tiene). Finalmente este es un buen ejemplo de que por que pgsql dice que "implementa un subconjunto extendido de SQL92", no hay trigger en SQL92, son del SQL3.
143
144
145
Synopsis
int SPI_connect(void)
Inputs
None
Outputs
int Return status SPI_OK_CONNECT if connected SPI_ERROR_CONNECT if not connected
146
Description
SPI_connect opens a connection to the Postgres backend. You should call this
function if you will need to execute queries. Some utility SPI functions may be called from un-connected procedures. You may get SPI_ERROR_CONNECT error if SPI_connect is called from an already connected procedure - e.g. if you directly call one procedure from another connected one. Actually, while the child procedure will be able to use SPI, your parent procedure will not be able to continue to use SPI after the child returns (if SPI_finish is called by the child). Its bad practice.
Usage
XXX thomas 1997-12-24
Algorithm
SPI_connect performs the following:
Initializes the SPI internal structures for query execution and memory management.
147
SPI_nish
Nombre
SPI_finish Disconnects your procedure from the SPI manager.
Synopsis
SPI_finish(void)
Inputs
None
Outputs
int SPI_OK_FINISH if properly disconnected SPI_ERROR_UNCONNECTED if called from an un-connected procedure
Description
SPI_finish closes an existing connection to the Postgres backend. You should call
this function after completing operations through the SPI manager. You may get the error return SPI_ERROR_UNCONNECTED if SPI_finish is called without having a current valid connection. There is no fundamental problem with this; it means that nothing was done by the SPI manager.
148
Usage
SPI_finish must be called as a nal step by a connected procedure or you may get unpredictable results! Note that you can safely skip the call to SPI_finish if you
Algorithm
SPI_finish performs the following:
Disconnects your procedure from the SPI manager and frees all memory allocations made by your procedure via palloc since the SPI_connect. These allocations cant be used any more! See Memory management.
SPI_exec
Nombre
SPI_exec Creates an execution plan (parser+planner+optimizer) and executes a query.
Synopsis
SPI_exec(query , tcount)
149
Inputs
char *query String containing query plan int tcount Maximum number of tuples to return
Outputs
int SPI_OK_EXEC if properly disconnected SPI_ERROR_UNCONNECTED if called from an un-connected procedure SPI_ERROR_ARGUMENT if query is NULL or tcount < 0. SPI_ERROR_UNCONNECTED if procedure is unconnected. SPI_ERROR_COPY if COPY TO/FROM stdin. SPI_ERROR_CURSOR if DECLARE/CLOSE CURSOR, FETCH. SPI_ERROR_TRANSACTION if BEGIN/ABORT/END. SPI_ERROR_OPUNKNOWN if type of query is unknown (this shouldnt occur). If execution of your query was successful then one of the following (non-negative) values will be returned: SPI_OK_UTILITY if some utility (e.g. CREATE TABLE ...) was executed SPI_OK_SELECT if SELECT (but not SELECT ... INTO!) was executed SPI_OK_SELINTO if SELECT ... INTO was executed SPI_OK_INSERT if INSERT (or INSERT ... SELECT) was executed SPI_OK_DELETE if DELETE was executed SPI_OK_UPDATE if UPDATE was executed
150
Description
SPI_exec creates an execution plan (parser+planner+optimizer) and executes the
Usage
This should only be called from a connected procedure. If tcount is zero then it executes the query for all tuples returned by the query scan. Using tcount > 0 you may restrict the number of tuples for which the query will be executed. For example,
SPI_exec ("insert into table select * from table", 5);
will allow at most 5 tuples to be inserted into table. If execution of your query was successful then a non-negative value will be returned.
Nota: You may pass many queries in one string or query string may be re-written by RULEs. SPI_exec returns the result for the last query executed.
The actual number of tuples for which the (last) query was executed is returned in the global variable SPI_processed (if not SPI_OK_UTILITY). If SPI_OK_SELECT returned and SPI_processed > 0 then you may use global pointer SPITupleTable *SPI_tuptable to access the selected tuples: Also NOTE, that SPI_finish frees and makes all SPITupleTables unusable! (See Memory management).
SPI_exec may return one of the following (negative) values:
SPI_ERROR_ARGUMENT if query is NULL or tcount < 0. SPI_ERROR_UNCONNECTED if procedure is unconnected. SPI_ERROR_COPY if COPY TO/FROM stdin. SPI_ERROR_CURSOR if DECLARE/CLOSE CURSOR, FETCH. SPI_ERROR_TRANSACTION if BEGIN/ABORT/END.
151
Algorithm
SPI_exec performs the following:
Disconnects your procedure from the SPI manager and frees all memory allocations made by your procedure via palloc since the SPI_connect. These allocations cant be used any more! See Memory management.
SPI_prepare
Nombre
SPI_prepare Connects your procedure to the SPI manager.
Synopsis
SPI_prepare(query , nargs, argtypes)
Inputs
query Query string
152
nargs Number of input parameters ($1 ... $nargs - as in SQL-functions) argtypes Pointer list of type OIDs to input arguments
Outputs
void * Pointer to an execution plan (parser+planner+optimizer)
Description
SPI_prepare creates and returns an execution plan (parser+planner+optimizer) but
doesnt execute the query. Should only be called from a connected procedure.
Usage
nargs is number of parameters ($1 ... $nargs - as in SQL-functions), and nargs may be 0 only if there is not any $1 in query. Execution of prepared execution plans is sometimes much faster so this feature may be useful if the same query will be executed many times. The plan returned by SPI_prepare may be used only in current invocation of the procedure since SPI_finish frees memory allocated for a plan. See SPI_saveplan. If successful, a non-null pointer will be returned. Otherwise, youll get a NULL plan. In both cases SPI_result will be set like the value returned by SPI_exec, except that it is set to SPI_ERROR_ARGUMENT if query is NULL or nargs < 0 or nargs > 0 && argtypes is NULL.
153
SPI_saveplan
Nombre
SPI_saveplan Saves a passed plan
Synopsis
SPI_saveplan(plan)
Inputs
void *query Passed plan
Outputs
void * Execution plan location. NULL if unsuccessful. SPI_result SPI_ERROR_ARGUMENT if plan is NULL SPI_ERROR_UNCONNECTED if procedure is un-connected
154
Description
SPI_saveplan stores a plan prepared by SPI_prepare in safe memory protected from freeing by SPI_finish or the transaction manager.
In the current version of Postgres there is no ability to store prepared plans in the system catalog and fetch them from there for execution. This will be implemented in future versions. As an alternative, there is the ability to reuse prepared plans in the consequent invocations of your procedure in the current session. Use SPI_execp to execute this saved plan.
Usage
SPI_saveplan saves a passed plan (prepared by SPI_prepare) in memory protected from freeing by SPI_finish and by the transaction manager and returns a pointer to
the saved plan. You may save the pointer returned in a local variable. Always check if this pointer is NULL or not either when preparing a plan or using an already prepared plan in SPI_execp (see below).
Nota: If one of the objects (a relation, function, etc.) referenced by the prepared plan is dropped during your session (by your backend or another process) then the results of SPI_execp for this plan will be unpredictable.
155
SPI_execp
Nombre
SPI_execp Executes a plan from SPI_saveplan
Synopsis
SPI_execp(plan, values, nulls, tcount)
Inputs
void *plan Execution plan Datum *values Actual parameter values char *nulls Array describing what parameters get NULLs n indicates NULL allowed indicates NULL not allowed int tcount Number of tuples for which plan is to be executed
156
Outputs
int Returns the same value as SPI_exec as well as SPI_ERROR_ARGUMENT if plan is NULL or tcount < 0 SPI_ERROR_PARAM if values is NULL and plan was prepared with some parameters. SPI_tuptable initialized as in SPI_exec if successful SPI_processed initialized as in SPI_exec if successful
Description
SPI_execp stores a plan prepared by SPI_prepare in safe memory protected from freeing by SPI_finish or the transaction manager.
In the current version of Postgres there is no ability to store prepared plans in the system catalog and fetch them from there for execution. This will be implemented in future versions. As a work arround, there is the ability to reuse prepared plans in the consequent invocations of your procedure in the current session. Use SPI_execp to execute this saved plan.
Usage
If nulls is NULL then SPI_execp assumes that all values (if any) are NOT NULL.
Nota: If one of the objects (a relation, function, etc.) referenced by the prepared plan is dropped during your session (by your backend or another process) then the results of SPI_execp for this plan will be unpredictable.
157
SPI_copytuple
Nombre
SPI_copytuple Makes copy of tuple in upper Executor context
Synopsis
SPI_copytuple(tuple)
Inputs
HeapTuple tuple Input tuple to be copied
158
Outputs
HeapTuple Copied tuple non-NULL if tuple is not NULL and the copy was successful NULL only if tuple is NULL
Description
SPI_copytuple makes a copy of tuple in upper Executor context. See the section on
Memory Management.
Usage
TBD
SPI_modifytuple
Nombre
SPI_modifytuple Modies tuple of relation
Synopsis
SPI_modifytuple(rel, tuple , nattrs
159
Inputs
Relation rel
HeapTuple tuple Input tuple to be modied int nattrs Number of attribute numbers in attnum int * attnum Array of numbers of the attributes which are to be changed Datum * Values New values for the attributes specied char * Nulls Which attributes are NULL, if any
Outputs
HeapTuple New tuple with modications non-NULL if tuple is not NULL and the modify was successful NULL only if tuple is NULL
160
SPI_result SPI_ERROR_ARGUMENT if rel is NULL or tuple is NULL or natts 0 or attnum is NULL or SPI_ERROR_NOATTRIBUTE if there is an invalid attribute number in attnum (attnum 0 or >
Description
SPI_modifytuple Modies a tuple in upper Executor context. See the section on
Memory Management.
Usage
If successful, a pointer to the new tuple is returned. The new tuple is allocated in upper Executor context (see Memory management). Passed tuple is not changed.
SPI_fnumber
Nombre
SPI_fnumber Finds the attribute number for specied attribute
Synopsis
SPI_fnumber(tupdesc, fname)
161
Inputs
TupleDesc tupdesc Input tuple description char * fname Field name
Outputs
int Attribute number Valid one-based index number of attribute SPI_ERROR_NOATTRIBUTE if the named attribute is not found
Description
SPI_fnumber returns the attribute number for the attribute with name in fname.
Usage
Attribute numbers are 1 based.
162
SPI_fname
Nombre
SPI_fname Finds the attribute name for the specied attribute
Synopsis
SPI_fname(tupdesc, fname)
Inputs
TupleDesc tupdesc Input tuple description char * fnumber Attribute number
Outputs
char * Attribute name NULL if fnumber is out of range SPI_result set to SPI_ERROR_NOATTRIBUTE on error
163
Description
SPI_fname returns the attribute name for the specied attribute.
Usage
Attribute numbers are 1 based.
Algorithm
Returns a newly-allocated copy of the attribute name.
SPI_getvalue
Nombre
SPI_getvalue Returns the string value of the specied attribute
Synopsis
SPI_getvalue(tuple, tupdesc, fnumber )
164
Inputs
HeapTuple tuple Input tuple to be examined TupleDesc tupdesc Input tuple description int fnumber Attribute number
Outputs
char * Attribute value or NULL if attribute is NULL fnumber is out of range (SPI_result set to SPI_ERROR_NOATTRIBUTE) no output function available (SPI_result set to SPI_ERROR_NOOUTFUNC)
Description
SPI_getvalue returns an external (string) representation of the value of the specied
attribute.
Usage
Attribute numbers are 1 based.
165
Algorithm
Allocates memory as required by the value.
SPI_getbinval
Nombre
SPI_getbinval Returns the binary value of the specied attribute
Synopsis
SPI_getbinval(tuple, tupdesc, fnumber , isnull)
Inputs
HeapTuple tuple Input tuple to be examined TupleDesc tupdesc Input tuple description int fnumber Attribute number
166
Outputs
Datum Attribute binary value bool * isnull ag for null value in attribute SPI_result SPI_ERROR_NOATTRIBUTE
Description
SPI_getbinval returns the binary value of the specied attribute.
Usage
Attribute numbers are 1 based.
Algorithm
Does not allocate new space for the binary value.
167
SPI_gettype
Nombre
SPI_gettype Returns the type name of the specied attribute
Synopsis
SPI_gettype(tupdesc, fnumber )
Inputs
TupleDesc tupdesc Input tuple description int fnumber Attribute number
Outputs
char * The type name for the specied attribute number SPI_result SPI_ERROR_NOATTRIBUTE
168
Description
SPI_gettype returns a copy of the type name for the specied attribute.
Usage
Attribute numbers are 1 based.
Algorithm
Does not allocate new space for the binary value.
SPI_gettypeid
Nombre
SPI_gettypeid Returns the type OID of the specied attribute
Synopsis
SPI_gettypeid(tupdesc, fnumber )
169
Inputs
TupleDesc tupdesc Input tuple description int fnumber Attribute number
Outputs
OID The type OID for the specied attribute number SPI_result SPI_ERROR_NOATTRIBUTE
Description
SPI_gettypeid returns the type OID for the specied attribute.
Usage
Attribute numbers are 1 based.
Algorithm
TBD
170
SPI_getrelname
Nombre
SPI_getrelname Returns the name of the specied relation
Synopsis
SPI_getrelname(rel)
Inputs
Relation rel Input relation
Outputs
char * The name of the specied relation
Description
SPI_getrelname returns the name of the specied relation.
171
Usage
TBD
Algorithm
Copies the relation name into new storage.
SPI_palloc
Nombre
SPI_palloc Allocates memory in upper Executor context
Synopsis
SPI_palloc(size)
Inputs
Size size Octet size of storage to allocate
172
Outputs
void * New storage space of specied size
Description
SPI_palloc allocates memory in upper Executor context. See section on memory
management.
Usage
TBD
SPI_repalloc
Nombre
SPI_repalloc Re-allocates memory in upper Executor context
Synopsis
SPI_repalloc(pointer , size)
173
Inputs
void * pointer Pointer to existing storage Size size Octet size of storage to allocate
Outputs
void * New storage space of specied size with contents copied from existing area
Description
SPI_repalloc re-allocates memory in upper Executor context. See section on
memory management.
Usage
TBD
174
SPI_pfree
Nombre
SPI_pfree Frees memory from upper Executor context
Synopsis
SPI_pfree(pointer )
Inputs
void * pointer Pointer to existing storage
Outputs
None
Description
SPI_pfree frees memory in upper Executor context. See section on memory
management.
175
Usage
TBD
176
SPI automatically frees memory allocated during execution of a query when this query is done!
14.5. Examples
This example of SPI usage demonstrates the visibility rule. There are more complex examples in in src/test/regress/regress.c and in contrib/spi. This is a very simple example of SPI usage. The procedure execq accepts an SQL-query in its rst argument and tcount in its second, executes the query using SPI_exec and returns the number of tuples for which the query executed:
#include "executor/spi.h" /* this is what you need to work with SPI */ int execq(text *sql, int cnt); int execq(text *sql, int cnt) { int ret; int proc = 0;
177
SPI_connect(); ret = SPI_exec(textout(sql), cnt); proc = SPI_processed; /* * If this is SELECT and some tuple(s) fetched * returns tuples to the caller via elog (NOTICE). */ if ( ret == SPI_OK_SELECT && SPI_processed > 0 ) { TupleDesc tupdesc = SPI_tuptable->tupdesc; SPITupleTable *tuptable = SPI_tuptable; char buf[8192]; int i; for (ret = 0; ret < proc; ret++) { HeapTuple tuple = tuptable->vals[ret]; for (i = 1, buf[0] = 0; i <= tupdesc->natts; i++) sprintf(buf + strlen (buf), " %s%s", SPI_getvalue(tuple, tupdesc, i), (i == tupdesc->natts) ? " " : " |"); elog (NOTICE, "EXECQ: %s", buf); } } SPI_finish(); return (proc); }
178
create function execq (text, int4) returns int4 as ...path_to_so language c; vac=> select execq(create table a (x int4), 0); execq --0 (1 row) vac=> insert into a values (execq(insert into a values (0),0)); INSERT 167631 1 vac=> select execq(select * from a,0); NOTICE:EXECQ: 0 < inserted by execq NOTICE:EXECQ: execq --2 (1 row) vac=> select execq(insert into a select x + 2 from a,1); execq --1 (1 row) vac=> select execq(select * from a, 10); NOTICE:EXECQ: 0 NOTICE:EXECQ: NOTICE:EXECQ: execq --3 (1 row) 1 2 < 0 + 2, only one tuple inserted - as specified 1 < value returned by execq and inserted by upper INSERT
179
vac=> delete from a; DELETE 3 vac=> insert into a values (execq(select * from a, 0) + 1); INSERT 167712 1 vac=> select * from a; x 1 < no tuples in a (0) + 1 (1 row) vac=> insert into a values (execq(select * from a, 0) + 1); NOTICE:EXECQ: 0 INSERT 167713 1 vac=> select * from a; x 1 2 < there was single tuple in a + 1 (2 rows) This demonstrates data changes visibility rule: select execq(select * from a, 0) * x from a;
vac=> insert into a NOTICE:EXECQ: 1 NOTICE:EXECQ: 2 NOTICE:EXECQ: 1 NOTICE:EXECQ: 2 NOTICE:EXECQ: 2 INSERT 0 2 vac=> select * from x 1 2 2 < 6 <
a;
180
(4 rows) cations
181
182
15.3. Interfaces
Las herramientas que Postgres proporciona para acceder a los objetos grandes, tanto en el backend como parte de funciones denidas por el usuario como en el frontend como parte de una aplicacin que utiliza la interfaz, se describen ms abajo. Para los usuarios familiarizados con Postgres 4.2, PostgreSQL tiene un nuevo conjunto de funciones que proporcionan una interfaz ms coherente.
Nota: Toda manipulacin de objetos grandes debe ocurrir dentro de una transaccin SQL. Este requerimiento es obligatorio a partir de Postgres v6.5, a pesar que en versiones anteriores era un requerimiento implcito, e ignorarlo resultar en un comportamiento impredecible.
La interfaz de objetos grandes en Postgres est diseada en forma parecida a la interfaz del sistema de archivos de Unix, con funciones anlogas como open(2), read(2), write(2), lseek(2), etc. Las funciones de usuario llaman a estas rutinas para obtener slo los datos de inters de un objeto grande. Por ejemplo, si existe un tipo de objeto grande llamado foto_sorpresa que almacena fotografas de caras, entonces puede denirse una funcin llamada barba sobre los datos de foto_sorpresa. Barba puede mirar el tercio inferior de una fotografa, y determinar el color de la barba que aparece, si es que hubiera. El contenido total del objeto grande no necesita ser puesto en un bfer, ni siquiera examinado por la funcin barba. Los objetos grandes pueden ser accedidos desde funciones C cargadas dinmicamente o programas clientes de bases de datos enlazados con la librera. Postgres proporciona un conjunto de rutinas que soportan la apertura, lectura, escritura, cierre y posicionamiento en objetos grandes.
183
crea un nuevo objeto grande. modo es una mscara de bits que describe distintos atributos del nuevo objeto. Las constantes simblicas listadas aqu se encuentran denidas en $PGROOT/src/backend/libpq/libpq-fs.h. El tipo de acceso (lectura, escritura, o ambos) se controla efectuando una operacin OR entre los bits INV_READ (lectura) e INV_WRITE (escritura). Si el objeto grande debe archivarse es decir, si versiones histricas del mismo deben moverse peridicamente a una tabla de archivo especial entonces el bit INV_ARCHIVE debe utilizarse. Los diecisis bits de orden bajo de la mscara constituyen el nmero de manejador de almacenamiento donde debe residir el objeto grande. Para otros sitios que no sean Berkeley, estos bits debern estar siempre en cero. Los comandos indicados ms abajo crean un objeto grande (invertido):
inv_oid = lo_creat(INV_READ|INV_WRITE|INV_ARCHIVE);
nombre_de_archivo especica la ruta y el nombre del archivo Unix que ser importado como objeto grande.
184
El argumento lobjId especica el Oid del objeto grande a exportar y el argumento nombre_de_archivo indica la ruta y nombre del archivo UNIX.
El argumento lobjId especica el Oid del objeto grande que se abrir. Los bits de modo controlan si el objeto se abre para lectura (INV_READ), escritura o ambos. Un objeto grande no puede abrirse antes de crearse. lo_open devuelve un descriptor de objeto grande para su uso posterior en lo_read, lo_write, lo_lseek, lo_tell, y lo_close.
escribe largo bytes desde buf al objeto grande fd . El argumento fd debi ser previamente devuelto por una llamada a lo_open. Devuelve el nmero de bytes escritos efectivamente. En caso de error, el valor de retorno es negativo.
185
lee largo bytes desde el objeto grande fd a buf . El argumento fd debi ser previamente devuelto por una llamada a lo_open. Devuelve el nmero de bytes ledos efectivamente. En caso de error, el valor de retorno es negativo.
Esta rutina mueve el puntero de posicin actual para el objeto grande descripto por fd a la nueva ubicacin especicada por el desplazamiento. Los valores vlidos para desde_donde son SEEK_SET, SEEK_CUR, y SEEK_END.
donde fd es un descriptor de objeto grande devuelto por lo_open. Si hay xito, lo_close devuelve cero. Si hay un error, el valor devuelto es negativo.
186
nombre contenido );
text, oid
INSERT INTO imagen (nombre, contenido) VALUES (imagen hermosa, lo_import(/etc/motd)); SELECT lo_export(imagen.contenido, "/tmp/motd") from imagen WHERE nombre = imagen hermosa;
187
* * IDENTIFICATION * /usr/local/devel/pglite/cvs/src/doc/manual.me,v 1.16 1995/09/ * *----------------------------------------*/ #include <stdio.h> #include "libpq-fe.h" #include "libpq/libpq-fs.h" #define BUFSIZE 1024
/* * importFile * importar el archivo "filename" en la base de datos como el objeto grande "lobjOid" * */ Oid importFile(PGconn *conn, char *filename) { Oid lobjId; int lobj_fd; char buf[BUFSIZE]; int nbytes, tmp; int fd;
/* * abrir el archivo a leer */ fd = open(filename, O_RDONLY, 0666); if (fd < 0) { /* error */ fprintf(stderr, "no se pudo abrir el archivo unix %s\n", filen } /* * crear el objeto grande */ lobjId = lo_creat(conn, INV_READ|INV_WRITE);
188
if (lobjId == 0) { fprintf(stderr, "no se pudo crear el objeto grande\n"); } lobj_fd = lo_open(conn, lobjId, INV_WRITE); /* * Leer desde el archivo Unix y escribir al archivo invertido */ while ((nbytes = read(fd, buf, BUFSIZE)) > 0) { tmp = lo_write(conn, lobj_fd, buf, nbytes); if (tmp < nbytes) { fprintf(stderr, "error al escribir el objeto grande\n"); } } (void) close(fd); (void) lo_close(conn, lobj_fd); return lobjId; } void pickout(PGconn *conn, Oid lobjId, int start, int len) { int lobj_fd; char* buf; int nbytes; int nread; lobj_fd = lo_open(conn, lobjId, INV_READ); if (lobj_fd < 0) { fprintf(stderr,"no se pudo abrir el objeto grande %d\n", lobjId); } lo_lseek(conn, lobj_fd, start, SEEK_SET); buf = malloc(len+1);
189
nread = 0; while (len - nread > 0) { nbytes = lo_read(conn, lobj_fd, buf, len - nread); buf[nbytes] = ; fprintf(stderr,"> %s", buf); nread += nbytes; } fprintf(stderr,"\n"); lo_close(conn, lobj_fd); } void overwrite(PGconn *conn, Oid lobjId, int start, int len) { int lobj_fd; char* buf; int nbytes; int nwritten; int i; lobj_fd = lo_open(conn, lobjId, INV_READ); if (lobj_fd < 0) { fprintf(stderr,"no se pudo abrir el objeto grande %d\n", lobjId); } lo_lseek(conn, lobj_fd, start, SEEK_SET); buf = malloc(len+1); for (i=0;i<len;i++) buf[i] = X; buf[i] = ; nwritten = 0; while (len - nwritten > 0) { nbytes = lo_write(conn, lobj_fd, buf + nwritten, len nwritten); nwritten += nbytes;
190
} fprintf(stderr,"\n"); lo_close(conn, lobj_fd); } /* * exportFile * exportar el objeto grande "lobjOid" al archivo "filename" * */ void exportFile(PGconn *conn, Oid lobjId, char *filename) { int lobj_fd; char buf[BUFSIZE]; int nbytes, tmp; int fd; /* * create an inversion "object" */ lobj_fd = lo_open(conn, lobjId, INV_READ); if (lobj_fd < 0) { fprintf(stderr,"no se pudo abrir el objeto grande %d\n", lobjId); } /* * open the file to be written to */ fd = open(filename, O_CREAT|O_WRONLY, 0666); if (fd < 0) { /* error */ fprintf(stderr, "no se pudo abrir el archivo unix %s\n", filename); } /*
191
* leer desde el archivo invertido y escribir al archivo Unix */ while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0) { tmp = write(fd, buf, nbytes); if (tmp < nbytes) { fprintf(stderr,"error al escribir %s\n", filename); } } (void) lo_close(conn, lobj_fd); (void) close(fd); return; } void exit_nicely(PGconn* conn) { PQfinish(conn); exit(1); } int main(int argc, char **argv) { char *in_filename, *out_filename; char *database; Oid lobjOid; PGconn *conn; PGresult *res;
192
} database = argv[1]; in_filename = argv[2]; out_filename = argv[3]; /* * set up the connection */ conn = PQsetdb(NULL, NULL, NULL, NULL, database); /* check to see that the backend connection was successfully made */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr,"Fall la conexin con la base de datos %s.\n", database); fprintf(stderr,"%s",PQerrorMessage(conn)); exit_nicely(conn); } res = PQexec(conn, "begin"); PQclear(res); printf("importando archivo %s\n", in_filename); lobjOid = importFile(conn, in_filename); */ lobjOid = lo_import(conn, in_filename); printf("como objeto grande %d.\n", lobjOid); printf("extrayendo los bytes 1000-2000 del objeto grande\n"); pickout(conn, lobjOid, 1000, 1000); printf("sobreescribiendo los bytes 1000-2000 del objeto grande con Xs\n"); overwrite(conn, lobjOid, 1000, 1000); */
/* /*
193
/*
printf("exportando el objeto grande al archivo %s\n", out_filen exportFile(conn, lobjOid, out_filename); */ lo_export(conn, lobjOid,out_filename); res = PQexec(conn, "end"); PQclear(res); PQfinish(conn); exit(0);
194
trasladar consultas al servidor de Postgres y recibir el resultado de esas consultas. libpq es tambin el mecanismo subyacente para muchas otras interfaces de aplicaciones de PostgreSQL, incluyendo libpq++ (C++), libpgtcl (Tcl), Perl, y ecpg. Algunos aspectos del comportamiento de libpq le resultarn de importancia si quiere utilizar uno de estos paquetes. Se incluyen tres programas cortos al nal de esta seccin para mostrarle como escribir programas que utilicen libpq. Hay varios ejemplos completos de aplicaciones con libpq en los siguientes directorios:
../src/test/regress ../src/test/examples ../src/bin/psql
Los programas cliente que utilicen libpq debern incluir el chero de cabeceras libpq-fe.h, y debern enlazarse con la biblioteca libpq.
195
objeto PGconn. Se debera llamar a la funcin PQstatus para comprobar si la conexin se ha realizado con xito antes de enviar consultas a traves del objeto de conexin.
PQconnectdb Realiza una nueva conexin al servidor de base de datos. PGconn *PQconnectdb(const char *conninfo)
Esta rutina abre una conexin a una base de datos utilizando los parmetros que se dan en la cadena conninfo. Contra lo que ocurre ms abajo con PQsetdbLogin(), los parmetros jados se pueden extender sin cambiar la rma de la funcin, de modo que el uso de bien esta rutina o bien las anlogas sin bloqueo PQconnetStart / PQconnectPoll resulta preferible para la programacin de las aplicaciones. La cadena pasada puede estar vara para utilizar as los parmetros de defecto, o puede contener uno o ms parmetros separados por espacios. Cada jacin de un parmetro tiene la forma keyword = value. (Para escribir un valor nulo o un valor que contiene espacin, se emplearn comillas simples, por ejemplo keyword = a value. Las comillas simples dentro de un valor se escribirn como \. Los espacios alrededor del signo igual son opcionales). Los parmetros reconocidos actualmente son:
host
Nombre del ordenador al que conectarse. Si se da una cadena de longitud distinta de cero, se utiliza comunicacin TCP/IP. El uso de este parmetro supone una bsqueda del nombre del ordenador. Ver hostaddr.
hostaddr
Direccin IP del ordenador al que se debe conectar. Debera estar en el formato estandar de nmeros y puntos, como se usan en las funciones de BSD inet_aton y otras. Si se especica una cadena de longitud distinta de cero, se emplea una comunicacin TCP/IP.
196
El uso de hostaddr en lugar de host permite a la aplicacin evitar la bsqueda del nombre de ordenador, lo que puede ser importante en aplicaciones que tienen una limitacin de tiempo. Sin embargo la autenticacin Kerberos necesita el nombre del ordenador. En este caso de aplica la siguiente secuencia. Si se especica host sin hostaddr, se fuerza la bsqueda del nombre del ordenador. Si se especica hostaddr sin host, el valor de hostaddr dar la direccin remota; si se emplea Kerberos, se buscar de modo inverso el nombre del ordenador. Si se dan tanto host como hostaddr, el valor de hostaddr dar la direccin remota; el valor de host se ignorar, a menos que se emplee Kerberos, en cuyo caso ese valor se utilizar para la autenticacin Kerberos. Ntese que libpq fallar si se pasa un nombre de ordenador que no sea el nombre de la mquina en hostaddr. Cuando no se empleen ni uno ni otro, libpq conectar utilizando un socket de dominio local.
port
Nmero del puerto para la conexin en el ordenador servidor, o extensin del nombre de chero del socket para conexin de dominio Unix.
dbname
Password que se deber utilizar si el servidor solicita una autenticacin con password.
options
197
tty
Un chero o tty para la salida de la depuracin opcional desde el servidor. Si no se especica ningn parmetro, se comprobarn las correspondiente variables de entorno. Si no se encuentran jadas, se emplean los valores de defecto codicadas en el programa. El valor devuelto es un puntero a una estructura abstracta que representa la conexin al servidor. Esta funcin no salva hebra.
PQsetdbLogin Realiza una nueva conexin al servidor de base de datos. PGconn *PQsetdbLogin(const const const const const const const char char char char char char char *pghost, *pgport, *pgoptions, *pgtty, *dbName, *login, *pwd)
Esta funcin es la predecesora de PQconnectdb, con un nmero jado de parmetros, pero con la misma funcionalidad. Esta funcin no salva hebra.
PQsetdb Realiza una nueva conexin al servidor de base de datos. PGconn *PQsetdb(char char char char char *pghost, *pgport, *pgoptions, *pgtty, *dbName)
Esta es una funcin que llama a PQsetdbLogin() con punteros nulos para los parmetros login y pwd. Se proporciona inicialmente para mantener compatibilidad con programas antigos.
198
Estas dos rutinas se utilizan para abrir una conexin al servidor de base de datos tal que la hebra de ejecucin de la aplicacin no queda bloqueada en el I/O remoto mientras lo hace. La conexin a la base de datos se realiza utilizando lo parmetros dados en la cadena conninfo, que se pasa a PQconnectStart. Esta cadena est en el mismo formato que se describi antes para PQconnectdb. Ni PQconnectStart ni PQconnectPoll bloquearn, aunque se exigen un cierto nmero de restriccines:
Los parmetros hostaddr y host se utilizan apropiadamente para asegurar que no se realizan consultas de nombre ni de nombre inverso. Vea la documentacin de estos parmetros bajo PQconnectdb antes para obtener ms detalles. Si llama a PQtrace, asegurese de que el objeto de la secuencia en la cual realiza usted un rastreo no bloquea. Asegurese usted mismo de que el socket se encuentra en el estado apropiado antes de llamar a PQconnetPoll, como se describe ms abajo.
Para empezar, llame conn=PQconnectStart("<connection_info_string>"). Si conn es NULL, libpq habr sido incapaz de crear una nueva estructura PGconn. De otro modo, se devolver un puntero PGconn valido (aunque todava no represente una conexin vlida a la base de datos). Al regreso de PQconnectStart, llame a status=PQstatus(conn). Si status es igual a CONNECTION_BAD, PQconnectStart habr fallado. Si PQconnectStart funciona con xito, el siguiente paso es comprobar libpq de forma que pueda proceder con la secuencia de conexin. Realice un bucle como sigue: Considere que por defecto una conexin se encuentra inactiva. Si el ltimo
199
PQconnectPoll devolvi PGRES_POLLING_ACTIVE, considere ahora que la conexin est activa. Si el ltimo PQconnectPoll(conn) devolvi PGRES_POLLING_READING, realice una select para leer en PQsocket(conn). Si devolvi PGRES_POLLING_WRITING, realice una select para escribir en PQsocket(conn). Si todava tiene que llamar a PQconnectPoll, es decir, tras llamar a PQconnectStart, comportese como si hubiera devuelto PGRES_POLLING_WRITING. Si la select muestra que el socket est preparado (ready), considerelo activo. Si ya ha decido que el esta conexin est activa, llame de nuevo a PQconnectPoll(conn). Si esta llamada devuelve PGRES_POLLING_OK, la conexin se habr establecido con xito. Ntese que el uso de select() para asegurar que el socket se encuentra listo es realmente un ejemplo; aquellos que dispongan de otras facilidades disponibles, como una llamada poll(), por supuesto pueden utilizarla en su lugar. En cualquier momento durante la conexin, se puede comprobar la situacin de esta conexin, llamando a PQstatus. Si el resultado es CONNECTION_BAD, el procedimiento de conexin habr fallado; si es CONNECTION_OK, la conexin est funcionando correctamente. Cualquiera de estas situaciones se puede detectar del mismo modo a partir del valor de retorno de PQconnectPoll, como ntes. Otras situaciones se pueden mostrar durante (y slo durante) un procedimiento de conexin asncrona. Estos indican la situacin actual del procedimiento de conexin, y se pueden utilizar para proporcionar informacin de retorno al usuario, por ejemplo. Estas situaciones pueden incluir:
CONNECTION_STARTED: Esperando que se realice una conexin. CONNECTION_MADE: Conexin OK; esperando para enviar. CONNECTION_AWAITING_RESPONSE: Esperando una respuesta del postmaster. CONNECTION_AUTH_OK: Recibida autenticacin, espera que arranque del servidor. CONNECTION_SETENV: Negociando el entorno.
200
Tngase en cuenta que, aunque estas constantes se conservarn (para mantener la compatibilidad), una aplicacin nunca debera basarse en la aparicin de las mismas en un orden particual, o en todos, o en que las situaciones siempre tengan un valor de estos documentados. Una aplicacin podra hacer algo as:
switch(PQstatus(conn)) { case CONNECTION_STARTED: feedback = "Connecting..."; break; case CONNECTION_MADE: feedback = "Connected to server..."; break; . . . default: feedback = "Connecting..."; }
Ntese que si PQconnectStart devuelve un puntero no nulo, deber usted llamar a PQnish cuando haya terminado con l, para disponer de la estructura y de cualquier bloque de memoria asociado. Se debe hacer esto incluso si ha fallado una llamada a PQconnectStart o a PQconnectPoll. PQconnectPoll actualmente bloquear si libpq se compila con USE_SSL denido. Esta restriccin se eliminar en el futuro. PQconnectPoll actualmente bloquear bajo Windows, a menos que libpq se compile con WIN32_NON_BLOCKING_CONNECTIONS denida. Este cdigo no se ha probado an bajo Windows, de forma que actualmente se encuentra desactivado por defecto. Esto podra cambiar en el futuro. Estas funciones dejarn el socket en un estado de no-bloqueo como si se hubiese llamado a PQsetnonblocking. Estas funciones no aseguran la hebra.
201
struct PQconninfoOption { char *keyword; /* Palabra clave de la opcin */ char *envvar; /* Nombre de la variable de entorno que recoge su valor si no se da expresamente */ char *compiled; /* Valor de defecto en el cdigo fuente si tampoco se asigna variable de entorno */ char *val; /* Valor de la opcin */ char *label; /* Etiqueta para el campo en el dilogo de conexin */ char *dispchar; /* Carcter a mostrar para este campo en un dilogo de conexin. Los valores son: "" Muestra el valor entrado tal cual es "*" Campo de Password - ocultar el valor "D" Opciones de depuracin - No crea un cam po por defecto */ int dispsize; /* Tamao del campo en carcteres para dialogo */ }
Devuelve la direccin de la estructura de opciones de conexin. Esta se puede utilizar para determinar todas las opciones posibles de PQconnectdb y sus valores de defecto actuales. El valor de retorno apunta a una matriz de estructuras PQconninfoOption, que termina con una entrada que tiene un puntero a NULL. Note que los valores de defecto (los campos "val") dependern de las variables de entorno y del resto del contexto. Cuando se le llame, se deben tratar los datos de las opciones de conexin como de slo lectura. Esta funcin no salva hebra.
202
Tngase en cuenta que incluso si fall el intento de conexin con el servidor (como se indicaba en PQstatus), la aplicacin deber llamar a PQnish para liberar la memoria utilizada por el objeto PGconn. No se debera utilizar el puntero PGconn una vez que se ha llamado a PQnish.
Esta funcin cerrar la conexin con el servidor e intentar establecer una nueva conexin al mismo postmaster, utilizando todos los mismos parmetros anteriores. Se puede utilizar para recuperar un error si una conexin que estaba trabajando se pierde.
de forma no bloqueante.
int PQresetStart(PGconn *conn); PostgresPollingStatusType PQresetPoll(PGconn *conn);
Estas funciones cerrarn la conexin al servidor e intentarn reestablecer una nueva conexin con el mismo postmaster, utilizando los mismos parmetros previamente utilizados. Esto puede ser utilizable para recuperaciones de errores si se pierde una conexin que estaba trabajando. Dieren de del anterior PQreset en que lo hacen de una forma no bloqueante. Estas funciones sufren las mismas restricciones que PQconnectStart y PQconnectPoll. Ejecute PQresetStart. Si devuelve 0, la limpieza ha fallado. Si devuelve 1, pruebe la limpieza utilizando PQresetPoll exactamente en la misma forma en que habra creado la conexin utilizando PQconnectPoll.
203
Los programadores de aplicaciones con libpq deberan ser cuidadosos de mantener la abstraccin de PGconn. Utilice las funciones siguientes para tomar el contenido de PGconn. Prohiba las referencias directas a los campos de la estructura PGconn, ya que estn sujetas a cambios en el futuro. (A partir de PostgreSQL 6.4, la denicin de la estructura PGconn incluso ya no se proporciona en libpq-fe.h. Si tiene usted viejas aplicaciones que acceden a campos de PGconn directamente, puede usted conservarlas utilizando para incluirla libpq-int.h tambin, pero le recomendamos encarecidamente que je pronto el cdigo).
PQdb
PQdb y las siguientes funciones devuelven los valores establecidos en la conexin. Estos valores se jan para toda la vida de PGconn. object.
PQuser
204
La situacin puede tomar varios valores diferentes. Sin embargo, slo dos de ellos tienen signicado fuera de un procedimiento de conexin asncrona: CONNECTION_OK o CONNECTION_BAD. Una buena conexin a la base de datos tiene es status CONNECTION_OK. Una conexin fallida se seala con la situacin CONNECTION_BAD. Normalmente, una situacin de OK se mantendr hasta PQfinish, pero un fallo de las comunicaciones puede probocar un cambio prematuro de la situacin a CONNECTION_BAD. En ese caso, la aplicacin podra intentar recuperar la comunicacin llamando a PQreset. Para averiguar otras posibles situacines que podran comprobarse, revise las entradas de PQconnectStart y PQconnectPoll.
Casi todas las funciones de libpq jarn el valor de PQerrorMessage si fallan. Tenga en cuenta que por convencin de libpq, un PQerrorMessage no vaco incluir un carcter "nueva lnea" nal.
PQbackendPID Devuelve el identicador (ID) del proceso del servidor que est
El PID del servidor es utilizable si se quiere hacer depuracin de errores y para comparar los mensajes de NOTIFY (que incluyen el PID del servidor que est realizando la noticacin). Tenga en cuenta que el PID corresponde a un proceso que se est ejecutando en el ordenador servidor de la base de datos, no en el ordenador local!
205
ambiente.
PGsetenvHandle *PQsetenvStart(PGconn *conn) PostgresPollingStatusType *PQsetenvPoll(PGsetenvHandle handle) void PQsetenvAbort(PGsetenvHandle handle)
Estas dos rutinas se pueden utilizar para re-ejecutar la negociacin del entorno que ocurre durante la apertura de una conexin al servidor de la base de datos. No tengo idea de para qu se puede aprovechar esto (la tiene alguien?), pero quiz resulte interesante para los usuarios poder recongurar su codicacin de carcteres en caliente, por ejemplo. Estas funciones no bloquean, sujeto a las restricciones aplicadas a PQconnectStart y PQconnectPoll. Para empezar, llame a handle=PQsetenvStart(conn), donde conn es una conexin abierta con el servidor de la base de datos. Si handle es NULL, libpq habr sido incapaz de situar una nueva estructura PGsetenvHandle. En otro vaso, se devuelve una estructura handle valida. (N. del T: Dejo la palabra handle como identicador de una estructura de datos la aplicacin, aunque evidentemente el usuario podr utilizar el nombre que desee. Conociendo los programas que yo programo, normalmente usara un nombre como con_servidor, por ejemplo). Este handle se piensa que sea opaco: slo debe utilizarlo para llamar a otras funciones de libpq (PQsetenvPoll, por ejemplo). Elija el procecimiento utilizando PQsetenvPoll, exactamente del mismo modo en que hubiese creado la conexin utilizando PQconnectPoll. El procedimiento se puede abortar en cualquier momento llamando a PQsetevnAbort (handle). Estas funciones no aseguran la hebra.
206
Esta funcin realiza las mismas tareas que PQsetenvStart y PQsetenvPoll, pero bloquea para hacerlo. Devuelve 1 en caso de xito, y 0 en caso de fracaso.
PQexec
Devuelve un puntero PGresult o, posiblemente, un puntero NULL. Generalmente devolver un puntero no nulo, excepto en condiciones de "fuera de memoria" (out-of-memory) o errores serios tales como la incapacidad de enviar la consulta al servidor. Si se devuelve un puntero nulo, se debera tratar de la misma forma que un resulado PGRES_FATAL_ERROR. Para conseguir ms informacin sobre el error, utilice PQerrorMessage.
La estructura PGresult encapsula el resultado devuelto por el servidor a la consulta. Los programadores de aplicaciones con libpq deberan mostrarse cuidadosos de mantener la abstraccin de PGresult. Prohiban la referencia directa a los campos de la estructura PGresult, porque estn sujetos a cambios en el futuro. (Incluso a partir de la versin 6.4 de Postgres, ha dejado de proporcionarse la denicin de PGresult en libpq-fe.h. Si tiene usted cdigo antguo que accede directamente a los campos de
207
PGresult, puede mantenerlo utilizando libpq-int.h tambin, pero le recomendamos que ajuste pronto el cdigo).
PQresultStatus
PGRES_EMPTY_QUERY La cadena enviada al servidor estaba vaca. PGRES_COMMAND_OK El comando se ha ejecutado con xito sin devolver datos. PGRES_TUPLES_OK La consulta se ha ejecutado con xito. PGRES_COPY_OUT Se ha arrancado la transmisin de datos desde el servidor
(Copy Out)
(Copy In)
Si al situacin del resultado es PGRES_TUPLES_OK, las rutinas descritas ms abajo se pueden utilizar para recuperar las tuplas devueltas por la consulta. Tengase en cuenta que una SELECT que intente recuperar 0 (cero) tuplas tambin mostrar PGRES_TUPLES_OK. PGRES_COMMAND_OK es para comandos que nunca devuelven tuplas (INSERT, UPDATE, etc). Una respuesta PGRES_EMPTY_QUERY indica que hay un error en el programa cliente.
PQresStatus
Convierte los tipos enumerados devueltos por PQresultStatus en una cadena constante que describe el cdigo de la situacin.
char *PQresStatus(ExecStatusType status);
PQresultErrorMessage
Devuelve el mensaje de error asociado con la consulta, o una cadena vaca si no hay error.
208
Siguiendo inmediatamente a una llamada a PQexec o PQgetResult, PQerrorMessage (sobre la conexin) devolver la misma cadena que PQresultErrorMessage (sobre el resultado). Sin embargo, un PGresult mantendr su mensaje de error hasta que sea destruido, mientras que el mensaje de error de la conexin cambiar cuando se realicen subsiguientes operaciones. Utilice PQresultErrorMessage cuando quiera conocer la situacin asociada a un PGresult particular; utilice PQerrorMessage cuando quiera conocer la situacin de la ltima operacin en la conexin.
PQntuples
PQbinaryTuples
Devuelve 1 si PGresult contiene datos binarios en las tuplas, y 0 si contiene datos ASCII.
int PQbinaryTuples(const PGresult *res);
Actualmente, los datos binarios de las tuplas solo los puede recuperar una consulta que extraiga datos de un cursor BINARY.
PQfname
Devuelve el nombre del campo (atributo) asociado con el indice de campo dado. Los ndices de campo empiezan con 0.
char *PQfname(const PGresult *res, int field_index);
PQfnumber
campo dado.
int PQfnumber(const PGresult *res, const char *field_name);
209
PQftype
Devuelve el tipo de campo asociado con el ndice del campo dado. El entero devuelto es una codicacin interna del tipo. Los ndices de campo empiezan con 0.
Oid PQftype(const PGresult *res, int field_num);
Puede usted consultar la tabla de sistema pg_type para obtener el nombre y propiedades de los diferentes tipos de datos. Los OID,s de los tipos de datos incluidos por defecto estn denidos en src/include/catalog/pg_type.h, en el rbol de fuentes del producto.
PQfsize
Devuelve el tamao en bytes del campo asociado con el ndice de campo dado. Los ndices de campo empiezan con 0.
int PQfsize(const PGresult *res, int field_index);
Qfsize devuelve el espacio reservado para este campo en una tupla de base de datos, en otras palabras, el tamao de la representacin binaria del tipo de datos en el servidor. Se devuelve -1 si el campo es de tamao variable.
PQfmod
Devuelve los datos de la modicacin especca del tipo del campo asociado con el ndice del campo dado. Los ndices de campo empiezan en 0.
int PQfmod(const PGresult *res, int field_index);
PQgetvalue
Devuelve un valor de un nico campo (atributo) de una tupla de PGresult. Los ndices de tuplas y de campos empiezan con 0.
char* PQgetvalue(const PGresult *res, int tup_num, int field_num);
Para la mayora de las consultas, el valor devuelto por PQgetvalue es una cadena ASCII terminada en un valor NULL que representa el valor del atributo. Pero si el valor de PQbinaryTuples() es 1, es valor que devuelve PQgetvalue es la representacin binaria del tipo en el formato interno del servidor (pero no incluye la
210
palabra del tamao, si el campo es de longitud variable). Es entonces responsabilidad del programador interpretar y convertir los datos en el tipo C correcto. El puntero devuelto por PQgetvalue apunta a una zona de almacenaje que forma parte de la estructura PGresult. No se la debera modicar, sino que se debera copiar explicitamente el valor a otra estructura de almacenamiento si se pretende utilizar una vez pasado el tiempo de vida de la estructura PGresult misma.
PQgetlength
Devuelve la longitud de un campo (atributo) en bytes. Los ndices de tupla y de campo empiezan en 0.
int PQgetlength(const PGresult *res, int tup_num, int field_num);
Esta es la longitud de los datos actuales para el valor de datos particular, es decir, el tamao del objeto apuntado por PQgetvalue. Notese que para valores representados en ASCII, este tamao tiene poco que ver con el tamao binario indicado por PQfsize.
PQgetisnull
Prueba un campo por si tiene un valor NULL. Los ndices de tupla y de campo empiezan con 0.
int PQgetisnull(const PGresult *res, int tup_num, int field_num);
Esta funcin devuelve 1 si el campo contiene un NULL, o 0 si contiene un valor no nulo.(Tenga en cuenta que PQgetvalue devolver una cadena vaca, no un puntero nulo, para un campo NULL).
PQcmdStatus
Devuelve la cadena de la situacin del comando para el comando SQL que gener el PGresult.
char * PQcmdStatus(const PGresult *res);
PQcmdTuples
211
Si el comando SQL que gener el PGresult era INSERT, UPDATE o DELETE, devolver una cadena que contiene el nmero de las afectadas. Si el comando era cualquier otro, devolver una cadena vaca.
PQoidValue
Devuelve el identicador de objeto (oid) de la tupla insertada, si el comando SQL era una INSERT. En caso contrario, devuelve InvalidOid.
Tanto el tipo Oid como la constante Invalid se denirn cuando incluya usted el chero de cabeceras libpq. Ambos sern de tipo entero (integer).
PQoidStatus
Devuelve una cadena con el identicador de objeto de la tupla insertada si el comando SQL era una INSERT. En otro caso devuelve una cadena vaca.
char * PQoidStatus(const PGresult *res);
Imprime todas las tuplas y, opcionalmente, los nombres de los atributos en la salida especicada.
void PQprint(FILE* fout, /* output stream */ const PGresult *res, const PQprintOpt *po); struct { pqbool
header;
pqbool align; pqbool standard; pqbool html3; pqbool expanded; pqbool pager; cesita. */ char *fieldSep; char *tableOpt; char *caption; char **fieldName; po alternativos. */
/* Imprime las cabeceras de los campos de salida y el contador de filas. */ /* Fija la alineacin de los campos. */ /* old brain dead format */ /* tabula la salida en html */ /* expande las tablas */ /* Usa el paginador para la salida si se ne/* /* /* /* separador de campos */ lo inserta en <tabla ...> de HTML */ HTML <caption> */ cadena terminada en null de nombres de cam-
212
} PQprintOpt;
psql utilizaba anteriormente esta funcin para imprimir los resultados de las consultas, pero este ya no es el caso, y esta funcin ya no se soporta activamente.
PQclear
Libera la zona de almacenamiento asociada con PGresult. Todas los resultados de las consultas deberan liberarse con PQclear cuando ya no son necesarios.
void PQclear(PQresult *res);
Puede usted conserar un objeto PGresult tanto tiempo como lo necesite; no se conservar si realiza una nueva consulta, e incluso si se pierde la conexin. Para evitar esto, debe usted llamar a PQclear. No hacerlo, repercute en prdidas de memoria en la aplicacin cliente.
PQmakeEmptyPGresult
propone.
PGresult* PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
Esta es una rutina interna de libpq para reservar e inicializar un objeto PGresult vaco. Est exportada porque algunas aplicaciones consideran interesante generar objetos resultado (particularmente objetos con situaciones de error) por si mismas. Si conn no es NULL y status indica un error, el mensaje de error (errorMessage) de la conexin en curso se copia en el PGresult. Recuerde que debera llamar a PQclear para este objeto tambin, del mismo modo que para un PGresult devuelto por la libpq misma.
213
PQexec
espera hasta que se completa la consulta. La aplicacin puede tener otro trabajo para hacer (como por ejemplo mantener una interfaz de usuario), en cuyo caso no se querr bloquear esperando la respuesta. Una vez que el control se pasa a PQexec, la aplicacin cliente tiene muy dicil intentar cancelar la consulta en curso. (Se puede hacer con un manipulador de seales, pero no de otra forma). slo puede devolver una estructura PGresult. Si la cadena de la consulta emitida contiene mltiples comands SQL, se perdern todos excepto el ltimo.
PQexec
Las aplicaciones que no se quieren encontrar con estas limitaciones, pueden utilizar en su lugar las funciones que subyacen bajo PQexec: PQsendQuery y PQgetResult. Para los programas antiguos que utilizaban esta funcionalidad utilizando PQputline y PQputnbytes y esperaban bloqueados el envo de datos del servidor, se aadi la funcin PQsetnonblocking. Las aplicaciones antguas pueden rechazar el uso de PQsetnonblocking y mantener el comportamiento anterior potencialmente bloquante. Los programas ms nuevos pueden utilizar PQsetnonblocking para conseguir una conexin con el servidor completamente no bloqueante.
Esta funcin asegura que las llamadas a PQputline, PQputnbytes, PQsendQuery y PQendcopy se ejecutarn sin bloquo, devolviendo en su lugar un error si necesitan ser llamadas de nuevo. Cuando una conexin a una base de datos se ha jado como no bloqueante, y se llama a PQexec, se cambiar el estado temporalmente a bloqueante, hasta que se completa la ejecucin de PQexec.
214
Se espera que en el prximo futuro, la mayora de libp se haga segura para la funcionalida de PQsetnonblocking.
PQisnonblocking
base de datos.
int PQisnonblocking(const PGconn *conn)
Devuelve TRUE si la conexin est jada a modo no bloqueante, y FALSE si est jada a bloqueante.
PQsendQuery
Enva una consulta a Postgres sin esperar los resultados. Devuelve TRUE si la consulta se despach correctamente, y FALSE si no fue as (en cuyo caso, utilice PQerrorMessage para obtener ms informacin sobre el fallo).
int PQsendQuery(PGconn *conn, const char *query);
Tras llamar correctamente a PQsendQuery, llame a PQgetResult una o ms veces para obtener el resultado de la consulta. No se debe volver a llamar a PQsendQuery en la misma conexin hasta que PQgetResult devuelva NULL, indicando que la consulta se ha realizado.
PQgetResult
Espera el siguiente resultado de una ejecucin previa de PQsendQuery, y lo devuelve. Se devuelve NULL cuando la consulta est completa y ya no habr ms resultados.
PGresult *PQgetResult(PGconn *conn);
Se debe llamar a PQgetResult repetidamente hasta que devuelva NULL, indicando que la consulta se ha realizado. (Si se la llama cuando no hay ninguna consulta activa, simplemente devolver NULL desde el principio). Cada uno de los resultados no nulos de PQgetResult debera procesarse utilizando las mismas funciones de acceso a PGresult previamente descritas. No olvide liberar cada objeto resultado con PQclear cuando lo haya hecho. Ntese que PQgetResult slo bloquear si hay una consulta activa y PQconsumeInput an no a leido los datos de respuesta necesarios.
215
Utilizando PQsendQuery y PQgetResult se resuelve uno de los problemas de PQexec: Si una cadena de consulta contiene mltiples comandos SQL, los resultados de esos comandos se pueden obtener individualmente. (Esto permite una forma sencilla de procesamiento paralelo: la aplicacin cliente puede estar manipulando los resultados de una consulta mientras el servidor sigue trabajando sobre consultas posteriores de la misma cadena de consulta). Sin embargo, la llamada a PQgetResult seguir probocando que el cliente quede bloqueado hasta que el servidor complete el siguiente comando SQL de la cadena. Esto se puede impedir con el uso adecuado de tres funciones ms:
PQconsumeInput
int PQconsumeInput(PGconn *conn); PQconsumeInput normalmente devuelve 1 indicando "no hay error", pero devuelve 0 s hay algn tipo de problema (en cuyo caso se ja PQerrorMessage). Tengase en
cuenta que el resultado no dice si se ha recogido algn dato de entrada. Tras llamar a PQconsumeInput, la aplicacin deber revisar PQisBusy y/o PQnotifies para ver si sus estados han cambiado.
PQconsumeInput se puede llamar incluso si la aplicacin an no est preparada
para recibir un resultado o una noticacin. La rutina leer los datos disponibles y los situar en un almacenamiento intermedio, probocando as una indicacin de preparado para leer a la funcin select(2) para que contine. La aplicacin puede por ello utilizar PQconsumeInput para limpiar la condicin select inmediatamente, y examinar despus los resultado tranquilamente.
PQisBusy
Devuelve 1 si una consulta est ocupada, es decir, si PQgetResult se quedara bloqueada esperando una entrada. Un 0 indica que se puede llamar a PQgetResult con la seguridad de no bloquear.
int PQisBusy(PGconn *conn); PQisBusy no intentar por s mismo leer los datos del servidor; por ello, se debe llamar primero a PQconsumeInput, o el estado ocupado no terminar nunca.
216
PQflush
Intenta lanzar cualquier dato encolado al servidor, y devuelve 0 si lo consigue (o si la cola de envo est vaca) y EOF si ha fallado por algn motivo.
int PQflush(PGconn *conn);
Es necesario llamar a PQflush en una conexin no bloqueante antes de llamar a select para determinar si ha llegado una respuesta. Una respuesta de 0 asegura que no hay datos encolados al servidor que no se hayan enviado todava. Solo las aplicaciones que han usado PQsetnonblocking necesitan esto.
PQsocket
Obtiene el nmero descriptor de chero para el socket de conexin con el servidor. Un descriptor vlido sera >= 0; un resultado de indica que no hay actualmente ninguna conexin con el servidor abierta.
int PQsocket(const PGconn *conn);
Se debera utilizar PQsocket para obtener el descriptor del socket del servidor para preparar la ejecucin de select(2). Esto permite a una aplicacin que utiliza conexin bloqueante esperar las respuestas u otras condiciones del servidor. Si el resultado de select(2) indica que los datos se pueden leer desde el socket del servidor, debera llamarse a PQconsumeInput para leer los datos; tras ello, se pueden utilizar PQisBusy, PQgetResult, y/o PQnotifies para procesar la respuesta. Las conexines no bloqueantes (que han utilizado PQsetnonblocking) no deberan utilizar select hasta que PQflush haya devuelto 0 indicando que no quedan datos almacenados esperando ser enviados al servidor.
Una aplicacin cliente tpica que utilice estas funciones tendr un bucle principal que utiliza select(2) para esperar todas las condiciones a las que debe responder. Una de estas condiciones ser la entrada disponible desde el servidor, lo que en terminos de select son datos legibles en el descriptor de chero identicado por PQsocket. Cuando el bucle principal detecta que hay preparada una entrada, debera llamar a PQconsumeInput para leer la entrada. Puede despus llamar a PQisBusy, seguido de
217
para detectar mensajes NOTIFY (ver "Noticacin Asncrona", ms abajo). Una aplicacin cliente que utilice PQsendQuery/PQgetResult tambin puede intentar cancelar una consulta que an se est procesando en el servidor. Requiere de Postgres que abandone el procesado de la consulta
PQrequestCancel
actual.
int PQrequestCancel(PGconn *conn);
Devuelve un valor 1 si la cancelacin se ha despachado correctamente, y 0 si no (y si no, PQerrorMessage dir porqu). Que se despache correctamente no garantiza que el requerimiento vaya a tener ningn efecto, sin embargo. Sin mirar el valor de retorno de PQrequestCancel, la aplicacin debe continuar con la secuencia de lectura de resultados normal, utilizando PQgetResult. Si la cancelacin ha sido efectiva, la consulta actual terminar rpidamente y devolver un resultado de error. Si fall la cancelacin (digamos que porque el servidor ya haba procesado la consulta), no se ver ningn resultado.
Ntese que si la consulta forma parte de una transaccin, la cancelacin abortar la transaccin completa.
PQrequestCancel se puede invocar de modo seguro desde un manipulador de seales. De esta forma, se puede utilizar en conjuncin con PQexec plano, si la
decisin de cancelar se puede tomar en un manipulador de seales. Por ejemplo, psql invoca a PQrequestCancel desde un manipulador de la seal SIGINT, permitiendo de este modo la cancelacin interactiva de consultas que l gestiona a travs de PQexec. Observese que PQrequestCancel no tendr efecto si la conexin no est abierta en ese momento, o si el servidor no est procesando una consulta.
218
PQfn
Requiere la ejecucin de una funcin de servidor a travs del interfaz de ruta rpida.
PGresult* PQfn(PGconn* conn, int fnid, int *result_buf, int *result_len, int result_is_int, const PQArgBlock *args, int nargs);
El argumento fnid es el identicador del objeto de la funcin que se debe ejecutar. result_buf es la zona de almacenamiento en la cual se debe situar el valor devuelto. El programa que hace la llamade deber haber reservado suciente espacio para almacenar el valor devuelto (no se comprueba!). La longitud del resultado real se devolver en el entero apuntado por result_len. Si se espera un resultado entero de 4 bytes, je result_is_int a 1; de otra forma, fjelo a 0. (Fijando result_is_int a 1 se indica a libpq que administre el valor balanceando los bytes si es necesario, de forma que se enve un valor int adecuado a la mquina cliente. Cuando result_is_int es 0, la cadena de bytes enviada por el servidor se devuelve sin modicar). args y nargs especican los argumentos a pasar a la funcin.
typedef struct { int len; int isint; union { int *ptr; int integer; } u;
219
resultStatus antes de utilizar el resultado. El programa que hace la llamada es responsable de liberar el PGresult con PQclear cuando ya no lo necesite.
Devuelve la siguiente nocacin de una lista de mensajes de noticacin an no manipulados recibidos desde el servidor. Devuelve NULL si no hay noticaciones pendientes. Una vez se devuelve una noticacin con PQnoties, esta se considera manipulada y se borrar de la lista de noticaciones.
PGnotify* PQnotifies(PGconn *conn); typedef struct PGnotify { char relname[NAMEDATALEN];
/* nombre de la relacin */
220
Tras procesar un objeto PGnotify devuelto por PQnotifies, asegurese de liberarlo con free() para impedir prdidas de memoria.
Nota: En PostgreSQL 6.4 y posteriores, el be_pid corresponde al servidor que realiza la noticacin, mientras que en versiones anteriores era siempre el PID del propio servidor.
previamente absorvidos por otra funcin libpq. En versiones previas de libpq, la nica forma de asegurar la recepcin a tiempo de mensajes NOTIFY era emitir constantemente consultas, incluso vacas, y comprobar entonces PQnotifies() tras cada PQexec(). Aunque esto funcionaba, se menospreciaba como una forma de malgastar poder de proceso. Una forma mejor de comprobar los mensajes NOTIFY cuando no se dispone de consultas utilizables es hacer una llamada PQconsumeInput(), y comprobar entonces PQnotifies(). Se puede usar select(2) para esperar la llegada de datos del servidor, no utilizando en este caso potencia de CPU a menos que se tenga algo que hacer. Ntese que esta forma funcionar correctamente mientras utilice usted PQsendQuery/PQgetResult o smplemente PQexec para las consultas. Debera usted, sin embargo, recordar comprobar PQnotifies() tras cada PQgetResult o PQexec para comprobar si ha llegado alguna noticacin durante el procesado de la consulta.
221
PQgetline
De modo similar a fgets(3), esta rutina copia longitud-1 carcteres en una cadena. Es como gets(3), sin embargo, en que el carcter "newline" de terminacin en un carcter nulo. PQgetline devuelve EOF en el EOF, 0 si se ha leido la lnea entera, y 1 si se ha llenado la zona de almacenamiento, pero an no se ha leido el n de lnea. Observese que la aplicacin deber comprobar si la nueva lnea consiste en los dos carcteres "\.", lo que indicara que el servidor ha terminado de enviar los resultados del comando copy. Si la aplicacin debera recibir lneas que son ms largas de longitud-1, deber tener cuidado de reconocer la lnea "\." correctamente (y no confunde, por ejemplo, el nal de una larga lnea de datos con la lnea de terminacin). El cdigo de src/bin/psql/copy.c contiene rutinas de ejemplo que manipulan correctamente el protocolo copy.
PQgetlineAsync
Lee una lnea de carcteres terminada con "newline" (transmitida por el servidor) en una zona de almacenamiento sin bloquear.
int PQgetlineAsync(PGconn *conn,
222
Esta rutina es similar a PQgetline, pero la pueden utilizar aplicaciones que leen datos de COPY asncronamente, ya que es sin bloqueo. Una vez realizado el comando COPY y obtenido una respuesta PGRES_COPY_OUT, la aplicacin debera llamar a PQconsumeInput y PQgetlineAsync hasta que se detecte la seal end-of-data. Contra PQgetline, esta rutina toma la responabilidad de detectar el EOF. En cada llamada, PQgetlineAsync devolver datos, siempre que tenga disponible una lnea de datos completa terminada en "newline" en el almacenamiento de entrada de libpq, o si la lnea de datos de entrada es demasiado larga para colocarla en el almacenamiento ofrecido por la aplicacin de llamada. En otro caso, no se devuelve ningn dato hasta que llega el resto de la lnea. La rutina devuelve -1 si reconoce el marcador end-of-copy-data, 0 si no tiene datos disponibles, o un nmero positivo que la el nmero de bytes de datos devueltos. Si se devuelve -1, la aplicacin que realiza la llamada deber llamar a PQendcopy, y volver despus al procesado normal. Los datos devueltos no se extendern ms all de un carcter "newline". Si es posible, se devolver una lnea completa cada vez. Pero si el almacenamiento ofrecido por la aplicacin que realiza la llamada es demasiado pequeo para recibir una lnea enviada por el servidor, se devolvern datos parciales. Se puede detectar esto comprobando si el ltimo byte devuelto es \n o no. La cadena devuelta no se termina con un carcter nulo. (Si quiere usted aadir un NULL de terminacin, asegurese de pasar una longitud del almacenamiento ms pequea que el tamao del almacenamiento de que realmente dispone).
PQputline
Enva una cadena terminada en carcter nulo al servidor. Devuelve 0 si todo funciona bien, y EOF si es incapaz de enviar la cadena.
int PQputline(PGconn *conn, const char *string);
Tenga en cuenta que la aplicacin debe envar explcitamente los dos caracteres \. en una lnea de nal para indicar al servidor que ha terminado de enviarle datos.
223
PQputnbytes
Enva una cadena terminada en un carcter no nulo al servidor. Devuelve 0 si todo va bien, y EOF si es incapaz de enviar la cadena.
int PQputnbytes(PGconn *conn, const char *buffer, int nbytes);
Esta funcin es exactamente igual que PQputline, excepto en que el almacenamiento de datos no necesita estar terminado en un carcter nulo, una vez que el nmero de bytes que se envan se especica directamente.
PQendcopy
Sincroniza con el servidor. Esta funcin espera hasta que el servidor ha terminado la copia. Debera utilizarse bien cuando se ha enviado la ltima cadena al servidor utilizando PQputline o cuando se ha recibido la ltima cadena desde el servidor utilizando PGgetline. Debe utilizarse, o el servidor puede recibir out of sync (fuera de sincrona) con el cliente. Una vez vuelve de esta funcin, el servidor est preparado para recibir la siguiente consulta. El valor devuelto es 0 si se completa con xito, o diferente de cero en otro caso.
int PQendcopy(PGconn *conn);
Como un ejemplo:
PQexec(conn, "create table foo (a int4, b char(16), d float8)"); PQexec(conn, "copy foo from stdin"); PQputline(conn, "3\thello world\t4.5\n"); PQputline(conn,"4\tgoodbye world\t7.11\n"); ... PQputline(conn,"\\.\n"); PQendcopy(conn);
Cuando se est utilizando PQgetResult, la aplicacin debera responder a un resultado PGRES_COPY_OUT ejecutando repetidamente PQgetline, seguido de PQendcopy una vez se detecta la lnea de terminacin. Debera entonces volver al bucle PQgetResult loop until hasta que PQgetResult devuelva NULL.
224
Similarmente, un resultado PGRES_COPY_IN se procesa por una serie de llamadas a PQputline seguidas por PQendcopy, y volviendo entonces al bucle PQgetResult. Esta organizacin asegurar que un comando de copia de entrada o de salida embebido en una serie de comandos SQL se ejecutar correctamente. Las aplicaciones antiguas habitualmente emiten una copia de entrada o de salida a travs de PQexec y asumen que la transaccin ha terminado tras el PQendcopy. Este mecanismo trabajar adecuadamente slo si la copia de entrada/salida es el nico comando SQL de la cadena de consulta.
PQuntrace
225
Por defecto, libpq imprime los mensajes de aviso del servidor as como unos pocos mensajes de error que genera por s mismo en stderr. Este comportamiento se puede sobreescribir suministrando una funcin de llamada de alarma que haga alguna otra cosa con los mensajes. La funcin de llamada de alarma utiliza como argumentos el texto del mensaje de error (que incluye un caracter nal de "newline"), y un puntero vaco que es el mismo pasado a PQsetNoticeProcessor. (Este puntero se puede utilizar para acceder a estados especcos de la aplicacin si se necesita). El procesador de avisos de defecto es simplemente:
static void defaultNoticeProcessor(void * arg, const char * message) { fprintf(stderr, "%s", message); }
Para utilizar un procesador de avisos especial, llame a PQsetNoticeProcessor inmediamente tras la creacin de un nuevo objeto PGconn. El valor devuelto es el puntero al procesador de avisos previo. Si proporciona usted un puntero de funcin de llamada a NUL, no se toma ninguna accin, sino que se devuelve el puntero activo.
226
PGHOST ja el nombre del del ordenador servidor. Si se especica una cadena de longitud distinta de 0, se utiliza comunicacin TCP/IP. Sin un nombre de ordenador, libpq conectar utilizando un socket del dominio Unix local. PGPORT ja el puerto de defecto o la extensin de chero del socket de dominio Unix local para la comunicacin con el servidor PostgreSQL. PGDATABASE ja el nombre de la base de datos PostgreSQL. PGUSER ja el nombre de usuario utilizado para conectarse a la base de datos y para autenticacin. PGPASSWORD ja la palabra de paso utilizada si el servidor solicita autenticacin por palabra clave. PGREALM sets the Kerberos realm to use with PostgreSQL, if it is different from the local realm. If PGREALM is set, PostgreSQL applications will attempt authentication with servers for this realm and use separate ticket les to avoid conicts with local ticket les. This environment variable is only used if Kerberos authentication is selected by the backend. PGOPTIONS ja opciones de ejecucin adicionales para el serviodor PostgreSQL. PGTTY ja el chero o tty en el que se mostrarn los mensajes de depuracin del servidor.
Las siguientes variables de entorno se pueden usar para especicar el comportamiento de defecto de los usuario para cada sesin de PostgreSQL:
227
PGDATESTYLE Fija el estilo de la representacin de fechas y horas. PGTZ Fija el valor de defecto para la zona horaria.
Las siguientes variables de entorno se pueden utilizar para especicar el comportamiento interno de defecto de cada sesin de PostgreSQL:
Reerase al comando SQL SET para obtener informacin sobre los valores correctos de estas variables de entorno.
228
/* * begin, by setting the parameters for a backend connection if the * parameters are null, then the system will try to use reasonable * defaults by looking up environment variables or, failing that, * using hardwired constants * * Se empieza fijando los parmetros de una conexin al servidor. Si los * parmetros son nulos, el sistema probar a utilizar valores de defect * razonables para buscar en las variables de entorno, y, si esto falla, * utilizar constantes includas directamente en el cdigo. */ pghost = NULL; /* host name of the backend server */ /* nombre del ordenador servidor. */ pgport = NULL; /* port of the backend server */ /* puerto asignado al servidor */ pgoptions = NULL; /* special options to start up the backend
229
* server */ /* opciones especiales para arrancar el servidor */ pgtty = NULL; ver */ vidor */ dbName = "template1"; /* make a connection to the database */ /* conectar con el servidor */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); /* * check to see that the backend connection was successfully made * se comprueba si la conexin se ha realizado con xito. */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr, "Connection to database %s failed.\n", dbName); fprintf(stderr, "%s", PQerrorMessage(conn)); exit_nicely(conn); } /* debug = fopen("/tmp/trace.out","w"); */ /* PQtrace(conn, debug); */ /* start a transaction block */ /* comienza un bloque de transaccin */ res = PQexec(conn, "BEGIN"); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "BEGIN command failed\n"); PQclear(res); exit_nicely(conn); } /* debugging tty for the backend ser/* tty (terminal) para depuracin del ser-
230
/* * should PQclear PGresult whenever it is no longer needed to avoid * memory leaks * se debera PQclear PGresult una vez que ya no es necesario, para impedir * prdidas de memoria. */ PQclear(res);
/* * fetch instances from the pg_database, the system catalog of * databases * se recogen las instancias a partir de pg_database, el catlogo de sistema de * bases de datos */ res = PQexec(conn, "DECLARE mycursor CURSOR FOR select * from pg_databas if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "DECLARE CURSOR command failed\n"); PQclear(res); exit_nicely(conn); } PQclear(res); res = PQexec(conn, "FETCH ALL in mycursor"); if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "FETCH ALL command didnt return tuples properly\n") /* no se han recogido tuplas de bases de datos */ PQclear(res); exit_nicely(conn); } /* first, print out the attribute names */ /* primero, se imprimen los nombres de los atributos */ nFields = PQnfields(res); for (i = 0; i < nFields; i++)
231
printf("%-15s", PQfname(res, i)); printf("\n\n"); /* next, print out the instances */ /* a continuacin, se imprimen las instancias */ for (i = 0; i < PQntuples(res); i++) { for (j = 0; j < nFields; j++) printf("%-15s", PQgetvalue(res, i, j)); printf("\n"); } PQclear(res); /* close the cursor */ /* se cierra el cursor */ res = PQexec(conn, "CLOSE mycursor"); PQclear(res); /* commit the transaction */ /* se asegura la transaccin */ res = PQexec(conn, "COMMIT"); PQclear(res); /* close the connection to the database and cleanup */ /* se cierra la conexin a la base de datos y se limpia */ PQfinish(conn); /* fclose(debug); */ }
232
* testlibpq2.c * Test of the asynchronous notification interface * Se comprueba el interfaz de notificaciones asncronas. * * Start this program, then from psql in another window do * NOTIFY TBL2; * Arranque este programa, y luego, desde psql en otra ventana ejecute * NOTIFY TBL2; * * Or, if you want to get fancy, try this: * Populate a database with the following: * O, si quiere hacer algo ms elegante, intente esto: * alimente una base de datos con lo siguiente: * * CREATE TABLE TBL1 (i int4); * * CREATE TABLE TBL2 (i int4); * * CREATE RULE r1 AS ON INSERT TO TBL1 DO * (INSERT INTO TBL2 values (new.i); NOTIFY TBL2); * * and do * y haga * * INSERT INTO TBL1 values (10); * */ #include <stdio.h> #include "libpq-fe.h" void exit_nicely(PGconn *conn) { PQfinish(conn); exit(1); }
233
main() { char
/* * begin, by setting the parameters for a backend connection if the * parameters are null, then the system will try to use reasonable * defaults by looking up environment variables or, failing that, * using hardwired constants * * Se empieza fijando los parmetros de una conexin al servidor. Si los * parmetros son nulos, el sistema probar a utilizar valores de defect * razonables para buscar en las variables de entorno, y, si esto falla, * utilizar constantes includas directamente en el cdigo. */ pghost = NULL; /* host name of the backend server */ /* nombre del ordenador del servidor */ pgport = NULL; /* port of the backend server */ /* puerto asignado al servidor */ pgoptions = NULL; /* special options to start up the backend * server */
234
/* opciones especiales para arrancar el servidor */ pgtty = NULL; ver */ vidor */ dbName = getenv("USER"); /* debugging tty for the backend ser/* tty (terminal) de depuracin del ser/* change this to the name of your test * database */ /* cambie esto para asignarlo al nom* base de datos de prueba */ /* make a connection to the database */ /* Se hace a conexin a la base de datos */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); /* * check to see that the backend connection was successfully made * Se comprueba si la conexin ha funcionado correctamente. */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr, "Connection to database %s failed.\n", dbName); fprintf(stderr, "%s", PQerrorMessage(conn)); exit_nicely(conn); } /* Se declara el inters en TBL2 */ res = PQexec(conn, "LISTEN TBL2"); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "LISTEN command failed\n"); PQclear(res); exit_nicely(conn); } /*
bre de su
235
* should PQclear PGresult whenever it is no longer needed to avoid * memory leaks * Se debera PQclear PGresult una vez que ya no es necesario, para * impedir prdidas de memoria. */ PQclear(res); while (1) {
/* * wait a little bit between checks; waiting with select() * would be more efficient. * esperamos un poquito entre comprobacines; esperar con select * sera ms eficiente. */ sleep(1); /* collect any asynchronous backend messages */ /* Se recoge asncronamente cualquier mensaje del servidor */ PQconsumeInput(conn); /* check for asynchronous notify messages */ /* Se comprueban los mensajes de notificacin asncrona */ while ((notify = PQnotifies(conn)) != NULL) { fprintf(stderr, "ASYNC NOTIFY of %s from backend pid %d received\n", notify->relname, notify->be_pid); free(notify); } } /* close the connection to the database and cleanup */ /* Se cierra la conexin con la base de datos y se lmpia */ PQfinish(conn);
236
populate a database by doing the following: Alimente una base de datos con lo siguiente: CREATE TABLE test1 (i int4, d float4, p polygon); INSERT INTO test1 values (1, 3.567, (3.0, 4.0, 1.0, 2.0)::polygon); INSERT INTO test1 values (2, 89.05, (4.0, 3.0, 2.0, 1.0)::polygon); the expected output is: La salida esperada es: tuple 0: got i = (4 bytes) 1, d = (4 bytes) 3.567000, p = (4 bytes) 2 points boundbox = (hi=3.000000/4.000000, lo = 1.000000,2.000000) tuple 1: got i = (4 bytes) 2, d = (4 bytes) 89.050003, p = (4 bytes) 2 points boundbox = (hi=4.000000/3.000000, lo = 2.000000,1.000000)
237
PGconn PGresult /* * * * * *
*pghost, *pgport, *pgoptions, *pgtty; *dbName; nFields; i, j; i_fnum, d_fnum, p_fnum; *conn; *res;
begin, by setting the parameters for a backend connection if the parameters are null, then the system will try to use reasonable defaults by looking up environment variables or, failing that, using hardwired constants
238
* Se empieza fijando los parmetros de una conexin al servidor. Si los * parmetros son nulos, el sistema probar a utilizar valores de defect * razonables para buscar en las variables de entorno, y, si esto falla, * utilizar constantes includas directamente en el cdigo. */ pghost = NULL; /* host name of the backend server */ /* nombre de ordenador del servidor */ pgport = NULL; /* port of the backend server */ /* puerto asignado al servidor. */ pgoptions = NULL; /* special options to start up the backend * server */ /* opciones especiales para arrancar el servidor */ pgtty = NULL; /* debugging tty for the backend server */ /* tty (terminal)) para depurar el servidor */ dbName = getenv("USER"); /* change this to the name of your test * database */ /* cambie esto al nombre de su ba* prueba */ /* make a connection to the database */ /* Se hace la conexin a la base de datos */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); /* * check to see that the backend connection was successfully made * Se comprueba que la conexin se ha realizado correctamente */ if (PQstatus(conn) == CONNECTION_BAD) {
se de datos de
239
fprintf(stderr, "Connection to database %s failed.\n", dbName); fprintf(stderr, "%s", PQerrorMessage(conn)); exit_nicely(conn); } /* start a transaction block */ res = PQexec(conn, "BEGIN"); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "BEGIN command failed\n"); PQclear(res); exit_nicely(conn); } /* * should PQclear PGresult whenever it is no longer needed to avoid * memory leaks * Se debera PQclear PGresult una vez que ya no es necesario, para * evitar prdidas de memoria. */ PQclear(res);
/* * fetch instances from the pg_database, the system catalog of * databases * se recogen las instancias de pg_database, el catlogo de sistema de * bases de datos. */ res = PQexec(conn, "DECLARE mycursor BINARY CURSOR FOR select * from tes if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "DECLARE CURSOR command failed\n"); PQclear(res); exit_nicely(conn); }
240
PQclear(res);
res = PQexec(conn, "FETCH ALL in mycursor"); if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "FETCH ALL command didnt return tuples properly\n") /* no se ha recogido ninguna base de datos */ PQclear(res); exit_nicely(conn); } i_fnum = PQfnumber(res, "i"); d_fnum = PQfnumber(res, "d"); p_fnum = PQfnumber(res, "p"); for (i = 0; i < 3; i++) { printf("type[%d] = %d, size[%d] = %d\n", i, PQftype(res, i), i, PQfsize(res, i)); } for (i = 0; i < PQntuples(res); i++) { int *ival; float *dval; int plen; POLYGON *pval; /* we hard-wire this to the 3 fields we know about */ /* codificamos lo que sigue para los tres campos de los que * algo */ ival = (int *) PQgetvalue(res, i, i_fnum); dval = (float *) PQgetvalue(res, i, d_fnum); plen = PQgetlength(res, i, p_fnum); /* * plen doesnt include the length field so need to
241
* increment by VARHDSZ * plen no incluye el campo de longitud, por lo que necesitamo * incrementar con VARHDSZ */ pval = (POLYGON *) malloc(plen + VARHDRSZ); pval->size = plen; memmove((char *) &pval->npts, PQgetvalue(res, i, p_fnum), plen); printf("tuple %d: got\n", i); printf(" i = (%d bytes) %d,\n", PQgetlength(res, i, i_fnum), *ival); printf(" d = (%d bytes) %f,\n", PQgetlength(res, i, d_fnum), *dval); printf(" p = (%d bytes) %d points \tboundbox = (hi=%f/%f, lo = %f,%f PQgetlength(res, i, d_fnum), pval->npts, pval->boundbox.xh, pval->boundbox.yh, pval->boundbox.xl, pval->boundbox.yl); } PQclear(res); /* close the cursor */ /* Se cierra el cursor */ res = PQexec(conn, "CLOSE mycursor"); PQclear(res); /* commit the transaction */ /* Se asegura la transaccin res = PQexec(conn, "COMMIT"); PQclear(res);
*/
/* close the connection to the database and cleanup */ /* Se cierra la conexin a la base de datos y se limpia. PQfinish(conn); }
*/
242
243
permiten a los programas cliente conectarse al servidor de Postgres. Estas conexiones vienen de dos formas: una Clase de Base de Datos, y una clase de Objetos Grandes. La Clase de Base de datos est pensada para manipular una base de datos. Puede usted enviar toda suerte de consultas SQL al servidor Postgres, y recibir las repuestas del servidor. La Clase de Objetos Grandes est pensada para manipular los objetos grandes en la base de datos. Aunque una instancia de Objetos Grandes puede enviar consultas normales al servidor de Postgres, slo est pensado para consultas sencillas que no devuelven ningn dato. Un objeto grande se debera ver como una cadena de un chero. En el futuro, debera comportarse de forma muy prxima a las cadenas de chero de C++ cin, cout y cerr. Este captulo est basado en la documentacin para la librera C libpq. Al nal de esta seccin se listan tres programas cortos como ejemplo de programacin con libpq++ (aunque no necesariamente de una buena programacin). Hay muchos tipos de aplicaciones libpq++ en src/libpq++/examples, incluyendo el cdigo fuente de los tres ejemplos expuestos en este captulo.
244
Las siguientes variables de entorno se pueden utilizar para seleccionar valores de parmetros de conexin de defecto, que sern utilizados por PQconnectdb o PQsetdbLogin si no se ha especicado directamente ningn otro valor por parte del cdigo que realiza la llamada. Son utilizables para impedir la codicacin de nombres de base de datos en programas de aplicacin sencillos.
Nota: libpq++ utiliza slo variables de entorno o cadenas del tipo conninfo de PQconnectdb.
PGHOST ja el nombre del ordenador servidor de defecto. Si se especica una cadena de longitud distinta de 0, se utiliza comunicacin TCP/IP. Sin un nombre de host, libpq conectar utilizando una conexin (un socket) del dominio Unix local. PGPORT ja el puerto de defecto o la extensin del chero de conexin del dominio Unix local para la comunicacin con el servidor Postgres. PGDATABASE ja el nomber de la base de datos Postgres de defecto. PGUSER ja el nombre de usuario utilizado para conectarse a la base de datos y para la autenticacin. PGPASSWORD ja la palabra de paso utilizada si el servidor solicita autenticacin de la palabra de paso. PGREALM ja el reino Kerberos a utilizar con Postgres, si es diferente del reino local. Si se ja PGREALM, las aplicaciones Postgres intentarn la autenticacin con los servidores de este reino, y utilizarn cheros de ticket separados, para impedir conictos con los cheros de ticket locales. Esta variable de entorno slo se utiliza si el servidor selecciona la autenticacin Kerberos. PGOPTIONS ja opciones de tiempo de ejecucin adicionales para el servidor de Postgres.
245
PGTTY ja el chero o tty al cual el servidor enviar los mensajes de seguimiento de la ejecucin.
Las siguientes variables de entorno se pueden utilizar para especicar el comportamiento de defecto para los usuarios para cada sesin de Postgres:
Las siguientes variables de entorno se pueden utilizar para especicar el comportamiento interno de defecto para cada sesion de Postgres:
Encontrar informacin sobre los valores correctos de estas variables de entorno en el comando SET de SQL.
246
PgConnection realiza una nueva conexin a un servidor de base de datos. PgConnection::PgConnection(const char *conninfo)
Aunque habitualmente se le llama desde una de las clases de acceso, tambin es posible conectarse a un servidor creando un objeto PgConnection.
no.
int PgConnection::ConnectionBad()
247
Tras la creacin de PgDatabase, se debera comprobar para asegurarse de que la conexin se ha realizado con xito antes de enviar consultas al objeto. Se puede hacer facilmente recogiendo el status actual del objeto PgDatabase con los mtodos Status o ConnectionBad.
Exec Envia una consulta al servidor. Probblemente sea ms deseable utilizar una de las dos siguientes funciones. ExecStatusType PgConnection::Exec(const char* query)
Devuelve el resultado de la consulta. Se pueden esperar los siguientes resultados. PGRES_EMPTY_QUERY PGRES_COMMAND_OK, si la consulta era un comando PGRES_TUPLES_OK, si la consulta ha devuelto tuplas
248
ExecCommandOk Enva una consulta de comando sobre el servidor. int PgConnection::ExecCommandOk(const char *query)
ExecTuplesOk Enva una consulta de tuplas al servidor. int PgConnection::ExecTuplesOk(const char *query)
ErrorMessage Devuelve el texto del ltimo mensaje de error. const char *PgConnection::ErrorMessage()
consulta.
int PgDatabase::Tuples()
249
Si el nombre de campo no se corresponde con ninguno de los de la consulta se devuelve un valor de -1.
entero devuelto es una codicacin interna del tipo. Los ndices de campo empiezan en 0.
Oid PgDatabase::FieldType(int field_num)
entero devuelto es una codicacin interna del tipo. Los ndices de campo empiezan en 0.
Oid PgDatabase::FieldType(const char* field_name)
Devuelve el lugar ocupado por este campo en la tupla de base de datos, dando el nmero de campo. En otras palabras, el tamao de la representacin binaria en el servidor del tipo de datos. Devolver -1 si se trata de un campo de tamao variable.
Devuelve el espacio ocupado por este campo en la tupla de base de datos dando el nombre del campo. En otras palabras, el tamao de la representacin binaria del tipo
250
Para la mayora de las consultas, el valor devuelto por GetValue es una representacin en ASCII terminada con un null del valor del atributo. Pero si BinaryTuples() es VERDADERO, el valor que devuelve GetValue es la representacin binaria del tipo en el formato interno del servidor (pero sin incluir el tamao, en el caso de campos de longitud variable). Es responsabilidad del programador traducir los datos al tipo C correcto. El puntero de devuelve GetValue apunta al almacenamiento que es parte de la estructura PGresult. No se debera modicar, y se debera copiar explcitamente el valor a otro almacenamiento si se debe utilizar pasado el tiempo de vida de la estructura PGresult misma. BinaryTuples() no se ha implementado an.
Para la mayora de las consultas, el valor devuelto por GetValue es una representacin en ASCII terminada con un null del valor del atributo. Pero si BinaryTuples() es VERDADERO, el valor que devuelve GetValue es la representacin binaria del tipo en el formato interno del servidor (pero sin incluir el tamao, en el caso de campos de longitud variable). Es responsabilidad del programador traducir los datos al tipo C correcto. El puntero de devuelve GetValue apunta al almacenamiento que es parte de la estructura PGresult. No se debera modicar, y se debera copiar explcitamente el valor a otro almacenamiento si se debe utilizar pasado el tiempo de vida de la estructura PGresult misma. BinaryTuples() no se ha implementado an.
251
Esta es la longitud actual del valor particular del dato, que es el tamao del objeto apuntado por GetValue. Tenga en cuenta que para valores representados en ASCII, este tamao tiene poco que ver con el tamao binario indicado por PQfsize.
Esta es la longitud actual del valor particular del dato, que es el tamao del objeto apuntado por GetValue. Tenga en cuenta que para valores representados en ASCII, este tamao tiene poco que ver con el tamao binario indicado por PQfsize.
PrintTuples Imprime todas las tuplas y, opcionalmente, los nombres de los atributos en la corriente de salida especicada. void PgDatabase::PrintTuples(FILE *out = 0, int printAttName = 1, int terseOutput = 0, int width = 0)
252
Las aplicaciones con libpq++ son noticadas cada vez que un servidor al que estn conectadas recibe una noticacin asncrona. Sin embargo la comunicacin entre el servidor y la aplicacin cliente no es asncrona. La aplicacin con libpq++ debe llamar al servidor para ver si hay informacin de alguna noticacin pendiente. Tras la
253
ejecucin de una consulta, una aplicacin cliente puede llamar a PgDatabase::Notifies para ver si en ese momento se encuentra pendiente algn dato de noticacin desde el servidor. PgDatabase::Notifies devuelve la noticacin de una lista de noticaciones pendientes de manipular desde el servidor. La funcin devuelve NULL si no hay noticaciones pendientes en el servidor. PgDatabase::Notifies se comporta como el reparto de una pila. Una vez que PgDatabase::Notifies ha devuelto la noticacin, esta se considera manipulada y se elimina de la lista de
(transmitida por el servidor) en una zona de almacenamiento (un buffer) string de tamao length.
int PgDatabase::GetLine(char* string, int length)
254
Como la rutina de sistema de Unix fgets (3), esta rutina copia length-1 caracteres en string . Es como gets (3), sin embargo, en que convierte la terminacin "nueva lnea" en un caracter null.
PgDatabase::GetLine Devuelve EOF al nal de un chero, 0 si se ha leido la
lnea entera, y 1 si la zona de almacenamiento de ha llenado pero no se ha ledo an el carcter "nueva lnea" de terminacin. Ntese que la aplicacin debe comprobar si la nueva lnea consiste simplemente en nico punto ("."), lo que indicara que el servidor ha terminado de enviar el resultado de copy. Por ello, si la aplicacin siempre espera recibir lneas que tienen ms de length-1 caracteres de longitud, la aplicacin deber asegurarse de comprobar el valor de retorno de PgDatabase::GetLine muy cuidadosamente.
La aplicacin debe enviar explcitamente un nico punto (".") para indicar al servidor que ha terminado de enviar sus datos.
Esta funcin espera hasta que el servidor ha terminado de procesar el comando copy. Debera utilizarse bien cuando se ha enviado la ltima cadena al servidor utilizando PgDatabase::PutLine, bien cuando se ha recibido la ltima cadena desde el servidor utilizando PgDatabase::GetLine. Debe utilizarse, o el servidor puede detectar fuera de sincrona (out of sync) con la aplicacin cliente. Una vez vuelve de esta funcin, el servidor est preparado para recibir la siguiente consulta. El valor devuelto es 0 cuando se completa con xito, y distinto de cero en otro caso.
255
As an example:
PgDatabase data; data.Exec("create table foo (a int4, b char(16), d float8)"); data.Exec("copy foo from stdin"); data.putline("3\etHello World\et4.5\en"); data.putline("4\etGoodbye World\et7.11\en"); &... data.putline(".\en"); data.endcopy();
256
18.1. Comandos
Tabla 18-1. Comandos pgtcl Comando pg_connect pg_disconnect pg_conndefaults pg_exec pg_result pg_select pg_listen pg_lo_creat pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_lseek Descripcin abre una conexin al servidor backend cierra una conexin obtiene las opciones de conexin y sus valores por defecto enva una consulta al backend manipula los resultados de una consulta hace un bucle sobre el resultado de una declaracin SELECT establece una rellamada mensajes NOTIFY crea un objeto grande abre un objeto grande cierra un objeto grande lee un objeto grande escribe un objeto grande busca y se coloca sobre una posicin en un objeto grande
257
Descripcin devuelve la posicin de un objeto grande sobre la que se est borra un objeto grande importa un chero Unix a un objeto grande exporta un objeto grande a un chero Unix
Estos comandos se describen en otras pginas ms adelante. Las rutinas pg_lo* son interfaces a las caractersticas de objetos grandes de Postgres. Las funciones han sido diseadas para imitar a las funciones del sistema anlogas en el sistema de cheros de Unix. Las rutinas pg_lo* deberan usarse dentro de un bloque transaccional BEGIN/END porque el descripor de chero devuelto por pg_lo_open slo es vlido para la transaccin en curso. pg_lo_import y pg_lo_export DEBEN ser usados en un bloque de transaccin BEGIN/END.
18.2. Ejemplos
He aqu un pequeo ejemplo de cmo usar las rutinas:
# getDBs : # get the names of all the databases at a given host and port number # with the defaults being the localhost and port 5432 # return them in alphabetical order proc getDBs { {host "localhost"} {port "5432"} } { # datnames is the list to be result set conn [pg_connect template1 -host $host -port $port] set res [pg_exec $conn "SELECT datname FROM pg_database ORDER BY datname set ntups [pg_result $res -numTuples] for {set i 0} {$i < $ntups} {incr i} { lappend datnames [pg_result $res -getTuple $i] }
258
Synopsis
pg_connect -conninfo opcionesConexion pg_connect nombreDb [-host nombreHost] [-port numeroPuerto] [-tty pqtty ] [-options argumentosOpcionalesBackend ]
259
valor.
Outputs
dbHandle Si toda ha ido bien, se devuelve un handle para una conexin de base de datos. Los handles comienzan con el prejo "pgsql".
260
Descripcin
pg_connect abre una conexin al backend de Postgres.
Existen dos versiones de la sintaxis. En la vieja, cada posible opcin tiene un switch en la declaracin de pg_connect. En la nueva forma, se utiliza un string como opcin que contiene mltiples valores. Vea pg_conndefaults para encontrar informacin sobre las opciones disponibles en la nueva sintaxis.
Uso
XXX thomas 1997-12-24
pg_disconnect
Nombre
pg_disconnect cierra una conexin al servidor backend
Synopsis
pg_disconnect dbHandle
Inputs
dbHandle Especica un handle de base de datos vlido.
261
Outputs
Ninguno
Descripcin
pg_disconnect cierra una conexin al backend de Postgres.
pg_conndefaults
Nombre
pg_conndefaults obtiene informacin sobre los parmetros de la conexin por defecto
Synopsis
pg_conndefaults
Inputs
Ninguno
262
Outputs
option list El resultado es una lista que describe las opciones posibles de conexin y sus valores por defecto. Cada entrada en la lista es una sub-lista del formato siguiente: {optname label dispchar dispsize value} donde optname se utiliza como opcin en pg_connect -conninfo.
Descripcin
pg_conndefaults devuelve informacin sobre las opciones de conexin disponibles en pg_connect -conninfo y el valor por defecto actual de cada opcin.
Uso
pg_conndefaults
pg_exec
Nombre
pg_exec enva un string con una consulta al backend
263
Synopsis
pg_exec dbHandle stringConsulta
Inputs
dbHandle Especiva un handle vlido para una base de datos. stringConsulta Especica una consulta SQL vlida.
Outputs
handleResultado Se devolver un error Tcl si Pgtcl no pudo obtener respuesta del backend. De otro modo, se crea un objeto consulta como respuesta y se devuelve un handle para l. Este handle puede ser pasado a pg_result para obtener los resultados de la consulta.
Description
pg_exec presenta una consulta al backend de Postgres y devuelve un resultado. El
resultado de la consulta se encarga de manejar el comienzo con el handle de la conexin y aade un punto un nmero como resultado. Tenga en cuenta que la falta de un error Tcl no es una prueba de que la consulta ha tenido xito! Un mensaje de error devuelto por el backend ser procesado como
264
resultado de una consulta con un aviso de fallo, no generndose un error Tcl en pg_exec.
pg_result
Nombre
pg_result obtiene informacin sobre el resultado de una consulta
Synopsis
pg_result handleResult opcionResult
Inputs
handleResult Es el handle para el resultado de una consulta. opcionResult Especica una de las varias posibles opciones. Opciones -status el estado del resultado.
265
-error el mensaje de error, si el estado indica error; de otro modo, un string vaco. -conn la conexin que produjo el resultado. -oid si el comando fue un INSERT, el tuplo del OID insertado; de otro modo un string vaco. -numTuples el nmero de tuplos devueltos por la consulta. -numAttrs el nmero de atributos en cada tuplo. -assign nombreArray asigna el resultado a un array, usando la forma (numTuplo,nombreAtributo). -assignbyidx nombreArray ?appendstr? asigna los resultado a un array usando el primer atributo del valor y el resto de nombres de atributos como claves. Si appendstr es pasado, entonces es aadido a cada clave. Brevemente, todos excepto el primer campo de cada tuplo son almacenados en un array, usando una nomenclatura del tipo (valorPrimerCampo,nombreCampoAppendStr). -getTuple numeroTuplo devuelve los campos del tuplo indicado en una lista. Los nmeros de tuplo empiezan desde cero.
266
-tupleArray numeroTuplo nombreArray almacena los campos del tuplo en el array nombreArray, indexados por nombres de campo. Los nmero de tuplo empiezan desde cero. -attributes devuelve una lista con los nombre de los atributos del tuplo. -lAttributes devuelve una lista de sub-listas {nombre tipo tamao} por cada atributo del tuplo. -clear elimina el objeto consulta resultante.
Outputs
el resultado depende de la opcin elegida, como se describi ms arriba.
Descripcin
pg_result devuelve informacin acerca del resultado de una consulta creada por un pg_exec anterior.
Puede mantener el resultado de una consulta en tanto en cuanto lo necesite, pero cuando haya terminado con l asegrese de liberarlo ejecutando pg_result -clear. clear. De otro modo, tendr un "agujero" en la memoria y Pgtcl mostrar mensajes indicando que ha creado demasiados objetos consulta.
267
pg_select
Nombre
pg_select hace un bucle sobre el resultado de una declaracin SELECT
Synopsis
pg_select handleBD stringConsulta varArray procConsulta
Inputs
handleBD Especica un handle vlido para una base de datos. stringConsulta Especica una consulta SQL select vlida. varArray Un array de variables para los tuplos devueltos. procConsulta Procedimiento que se ha ejecutado sobre cada tuplo encontrado.
268
Outputs
handleResult el resultado devuelto es un mensaje de error o un handle para un resultado de consulta.
Description
pg_select pg_select enva una consulta SELECT al backend de Postgres , y ejecuta
una porcin de cdigo que se le ha pasado por cada tuplo en el resultado de la consulta. El stringConsulta debe ser una declaracin SELECT. Cualquier otra cosa devuelve un error. La variable varArray es un nombre de array usado en el bucle. Por cada tuplo, varArray arrayVar se rellena con los valores del campo tuplo usando los nombres de campo como ndices del array. A partir de aqu procConsulta se ejecuta.
Uso
Esto funcionara si la tabla "table" tiene los campos "control" y "name" (y tal vez otros campos):
pg_select $pgconn "SELECT * from table" array { puts [format "%5d %s" array(control) array(name)] }
269
pg_listen
Nombre
pg_listen ja o cambia una rellamada para los mensajes NOTIFY asncronos
Synopsis
pg_listen dbHandle notifyName comandoRellamada
Inputs
dbHandle Especica un handle de base de datos vlido. notifyName Especica el nombre de la noticacin para empezar o parar de escuchar. comandoRellamada Si este parmetro se pasa con un valor no vaco, proporciona el comando a ejecutar cuando una noticacin vlida llegue.
Outputs
Ninguno
270
Description
pg_listen pg_listen crea, cambia o cancela una peticin para escuchar mensajes
NOTIFY asncronos desde el backend de Postgres. Con un parmetro comandoRellamada, la peticin se establecer o el string de comando de una peticin existente ser reemplazada . Sin ningn parmetro comandoRellamada, se cancelar una peticin anterior. Despus de que se establezca una peticin pg_listen, el string de comando especicado se ejecutar cuando un mensaje NOTIFY que lleve el nombre dado llegue desde el backend. Esto ocurre cuando cualquier aplicacin cliente de Postgres muestra un comando NOTIFY haciendo referencia a ese nombre. (Ntese que puede ser, aunque no obligatoriamente, el de una relacin existente en la base de datos). El string de comando se ejecuta desde el loop de espera de Tcl. Este es el estado de espera normal de una aplicacin escrita con Tk. En shells que no son Tk Tcl, puede ejecutar update o vwait para provocar que se introduzca el loop de espera. No debera invocar las declaraciones SQL LISTEN o UNLISTEN directamente cuando est usando pg_listen. Pgtcl se encarga de poner en marcha esas declaraciones por usted. Pero si usted quiere enviar un mensaje NOTIFY, invoque la declaracin SQL NOTIFY usando pg_exec.
pg_lo_creat
Nombre
pg_lo_creat crea un objeto grande
271
Synopsis
pg_lo_creat conn modo
Inputs
conn Especica una conexin vlida a una base de datos. modo Especica el modo de acceso para el objeto grande.
Outputs
idObjeto Es el oid (id del objeto) del objeto grande creado.
Descripcin
pg_lo_creat crea un objeto grande de inversin (Inversion Large Object).
Uso
modo puede ser cualquier agrupacin con OR de INV_READ, INV_WRITE e INV_ARCHIVE. El carcter delimitador de OR es "|".
[pg_lo_creat $conn "INV_READ|INV_WRITE"]
272
pg_lo_open
Nombre
pg_lo_open abre un objeto grande
Synopsis
pg_lo_open conn idObjeto modo
Inputs
conn Especica una conexin vlida a una base de datos. idObjeto Especica un oid vlido para un objeto grande. modo Especica el modo de acceso para el objeto grande.
273
Outputs
fd Un descriptor de chero para usar posteriormente en rutinas pg_lo*.
Descripcin
pg_lo_open abre un objeto grande de inversin (Inversion Large Object).
Uso
modo puede ser "r", "w" o "rw".
pg_lo_close
Nombre
pg_lo_close cierra un objeto grande
Synopsis
pg_lo_close conn fd
274
Inputs
conn Especica una conexin vlidad a una base de datos. fd Un descriptor de chero para ser usado posteriormente en rutinas pg_lo*.
Outputs
Ninguno
Descripcin
pg_lo_close cierra un objeto grande de inversin (Inversion Large Object).
Uso
pg_lo_read
Nombre
pg_lo_read lee un objeto grande
275
Synopsis
pg_lo_read conn fd bufVar len
Inputs
conn Especica una conexin vlida a una base de datos. fd Descriptor de chero para el objeto grande tomado de pg_lo_open. bufVar Especica una variable de buffer vlida para contener el segmento del objeto grande. len Especica el tamao mximo permitido para el segmento del objeto grande.
Outputs
Ninguno
Descripcin
pg_lo_read lee la mayor parte de los bytes de len y lo copia a la variable bufVar .
276
Uso
bufVar debe ser un nombre de variable vlido.
pg_lo_write
Nombre
pg_lo_write escribe un objeto grande
Synopsis
pg_lo_write conn fd buf len
Inputs
conn Especica una conexin vlida a una base de datos. fd Descriptor de chero para el objeto grande tomado de pg_lo_open. buf Especica una variable string vlida para escribir en el objto grande. len Especica el tamao mximo del string a escribir.
277
Outputs
Ninguno
Descripcin
pg_lo_write escribe la mayor parte de len bytes a un objeto desde una variable
buf .
Usage
buf deber ser ser el string a escribir, no el nombre de la variable.
pg_lo_lseek
Nombre
pg_lo_lseek busca una posicin en un objeto grande
Synopsis
pg_lo_lseek conn fd offset whence
278
Inputs
conn Especica una conexin vlida a una base de datos. fd Descriptor de chero para el objeto tomado de pg_lo_open. offset Especica un offset en bytes en base cero. whence whence puede ser "SEEK_CUR", "SEEK_END" o "SEEK_SET"
Outputs
Ninguno
Descripcin
pg_lo_lseek se posiciona en offset bytes desde el comienzo,n o actual posicin
de un objeto grande.
Uso
whence puede ser "SEEK_CUR", "SEEK_END", o "SEEK_SET".
279
pg_lo_tell
Nombre
pg_lo_tell devuelve la posicin actual de bsqueda de un objeto grande
Synopsis
pg_lo_tell conn fd
Inputs
conn Especica una conexin vlida a una base de datos. fd Descriptor de chero del objeto tomado de pg_lo_open.
Outputs
offset Un offset en bytes en base zero adecuado para pg_lo_lseek.
280
Description
pg_lo_tell devuelve la posicin de bsqueda actual a offset en bytes desde el
Uso
pg_lo_unlink
Nombre
pg_lo_unlink borra un objeto grande
Synopsis
pg_lo_unlink conn idObjeto
Inputs
conn Especica una conexin vlida a una base de datos. idObjeto Es el identicador para un objeto grande. XXX Es esto igual que idObjetos en otras llamadas?? - thomas 1998-01-11
281
Outputs
Ninguno
Descripcin
pg_lo_unlink borra el objeto grande especicado.
Uso
pg_lo_import
Nombre
pg_lo_import importa un objeto grande desde un chero Unix
Synopsis
pg_lo_import conn nombreFichero
Inputs
conn Especica una conexin vlida a una base de datos.
282
Outputs
Ninguno XXX Devuelve esto un idObjeto? Es lo mismo que idObjeto en otras llamadas? thomas - 1998-01-11
Descripcin
pg_lo_import lee el chero especicado y pone el contenido del mismo en un objeto
grande.
Uso
pg_lo_import debe ser llamado dentro de un bloque de transaccin BEGIN/END.
pg_lo_export
Nombre
pg_lo_export exporta un objeto grande a un chero Unix
283
Synopsis
pg_lo_export conn idObjeto nombreFichero
Inputs
conn Especica una conexin vlida a una base de datos. idObjeto Identicador del objeto grande. XXX Es igual a idObjeto en otras llamadas?? thomas - 1998-01-11 nombreFichero Nombre del chero Unix.
Outputs
Ninguno. XXX Devuelve un idObjeto? Es esto igual a idObjeto en otras llamadas? thomas - 1998-01-11
Descripcin
pg_lo_export escribe el objeto grande especicado en un chero Unix.
Uso
pg_lo_export debe ser llamado dentro de un bloque de transaccin BEGIN/END.
284
285
Nombre
( );
287
ODBC (Open Database Connectivity / Conectividad Abierta para Bases de Datos) es un API abstracto que permite escribir aplicaciones que pueden interoperar con varios servidores RDBMS. ODBC facilita un interfaz de producto neutral entre aplicaciones de usuario nal y servidores de bases de datos, permitiendo ya sea a un usuario o desarrollador escribir aplicaciones transportables entre servidores de diferentes fabricantes.
19.1. Trasfondo
El API ODBC se conecta en la capa inferior de la aplicacin con una fuente de datos compatible con ODBC. sta (la fuente de datos) podra ser desde un chero de texto a una RDBMS Oracle o Postgres. El acceso en la capa inferior de aplicacin se produce gracias a drivers ODBC, o drivers especcos del fabricante que permiten el acceso a los datos. psqlODBC es un driver, junto con otros que estn disponibles, como los drivers ODBC Openlink. Cuando se escribe una aplicacin ODBC usted, debera ser capaz de conectar con cualquier base de datos, independientemente del fabricante, siempre y cuando el esquema de la base de datos sea el mismo. Por ejemplo. Usted podra tener servidoresMS SQL Server y Postgres que contuvieran exactamente los mismos datos. Usando ODBC, su aplicacin Windows podra hacer exactamente las mismas llamadas y la fuente de datos a nivel interno sera la misma (para la aplicacin cliente en Windows).
288
Insight Distributors (http://www.insightdist.com/) dan soporte continuo y actual a la distribucin psqlODBC. Suministran un FAQ (http://www.insightdist.com/psqlodbc/), sobre el desarrollo actual del cdigo base, y participan activamente en la lista de correo de interfaces (mailto:interfaces@postgresql.org).
Access, Delphi, y Visual Basic soportan directamente ODBC. Bajo C++, y en Visual C++, puede usar el API ODBC de C++. En Visual C++, puede usar la clase CRecordSet, la cual encapsula el API ODBC dentro de una clase MFC 4.2. Es el camino ms fcil si est desarrollando en C++ para Windows bajo Windows NT.
289
290
En la actualidad existen dos mtodos distintos para la construccin del driver en funcin de cmo se haya recibido y sus diferencias se reducen a dnde y cmo ejecutar congure y make. El driver puede ser construido en modo de equipo aislado, instalacin de slo cliente, o como parte de la distribucin Postgres. La instalacin aislada es conveniente si usted tiene aplicaciones clientes de ODBC en plataformas mltiples y heterogneas. La instalacin integrada es conveniente cuando las plataformas cliente y servidora son las mismas, o cuando cliente y servidor tienen conguraciones de ejecucin similares. Especcamente si ha recibido el driverpsqlODBC como parte de la distribucin Postgres (a partir de ahora se referenciar como "instalacin integrada") entonces podr congurar el driver ODBC desde el directorio principal de fuentes de la distribucin Postgres junto con el resto de las libreras. Si lo recibi como un paquete aislado, entonces podr ejecutar "congure" y "make" desde el directorio en el que desempaquet los fuentes. Instalacin integrada Este procedimiento es apropiado para la instalacin integrada. 1. Especicar el argumento-with-odbc en la lnea de comandos para src/congure:
% ./configure -with-odbc % make
2.
Una vez congurado, el driver ODBC ser construido e instalado dentro de las reas denidas para otros componentes del sistema Postgres. El chero de conguracin de instalacin ODBC ser colocado en el directorio principal del rbol de destino Postgres (POSTGRESDIR). Esto puede ser cambiado en la lnea de comandos de make como
% make ODBCINST=filename install
291
Instalacin Integrada Pre-v6.4 Si usted tiene una instalacin Postgres ms antigua que la v6.4, tiene disponible el rbol de fuentes original, y desea usar la versin ms actualizada del driver ODBC, entonces desear esta modalidad de instalacin. 1. 2. Copie el chero tar de salida a su sistema y desempaqutelo en un directorio vaco. Desde el directorio donde se encuentran los fuentes, teclee:
% ./configure % make % make POSTGRESDIR=PostgresTopDir install
3.
Si desea instalar los componentes en diferentes rboles, entonces puede especicar varios destinos explcitamente:
% make BINDIR=bindir stall LIBDIR=libdir
HEADERDIR=headerdir ODBCINST=instfil
Instalacin Aislada Una instalacin aislada no est congurada en la distribucin Postgres habitual. Debe realizarse un ajuste mejor para la construccin del driver ODBC para clientes mltiples y y heterogeneos que no tienen instalado un rbol de fuentes Postgres de forma local. La ubicacin por defecto para las libreras y cheros de cabecera y para la instalacin aislada es /usr/local/lib y /usr/local/include/iodbc, respectivamente. Existe otro chero de conguracin de sistema que se instala como /share/odbcinst.ini (si /share exists) o como /etc/odbcinst.ini (si /share no existe).
292
Nota: La instalacin de cheros en /share o /etc requiere privilegios de root. Muchas etapas de la instalacin de Postgres no necesitan de este requerimiento, y usted puede elegir otra ubicacin en que su cuenta de superusuario Postgres tenga permisos de escritura.
1.
La instalacin de la distribucin aislada puede realizarse desde la distribucin Postgres o puede ser obtenida a travs de Insight Distributors (http://www.insightdist.com/psqlodbc), los mantenedores actuales para distribuciones no Unix. Copie el chero zip o el chero tar comprimido en un directorio vaco. Si usa el paquete zip, descomprmalo con el comando
% unzip -a packagename
La opcin -a es necesaria para deshacerse de los pares CR/LF de DOS en los cheros fuente Si tiene el paquete tar comprimido, simplemente ejecute
tar -xzf packagename
a. 2. 3.
Para crear un chero tar para una instalacin aislada completa desde el rbol principal de fuentes de Postgres:
4. 5.
Copie el chero tar de salida al sistema de destino. Asegrese de transferirlo como un chero binario usando ftp. Desempaquete el chero tar en un directorio vaco.
293
6.
donde -prefix instala las bibliotecas y cheros de cabecera en los directorios rootdir /lib y rootdir /include/iodbc, y -with-odbc instala odbcinst.ini en el directorio especicado. Ntese que ambas opciones se pueden usar desde la construccin integrada pero tenga en cuenta que cuando se usan en la construccin integrada -prefix tambin se aplicar al resto de su instalacin Postgres. -with-odbc se aplica slo al chero de conguracin odbcinst.ini. 7. Compile and link the source code:
% make ODBCINST=instdir
Tambin puede obviar la ubicacin por defecto en la instalacin en la lnea de comandos de make. Esto slo se aplica a la instalacin de las libreras y los cheros de cabecera. Desde que el driver necesita saber la ubicacin del chero odbcinst.ini el intento de sustituir la variable de que especica el directorio de instalacin probablemente le causar quebraderos de cabeza. Es ms seguro y simple permitir al driver que instale el chero odbcinst.ini en el directorio por defecto o el directorio especicado por usted en en la lnea de comandos de la orden ./congure con with-odbc. 8. Instala el cdigo fuente:
% make POSTGRESDIR=targettree install
Para sustituir la librera y los directorios principales de instalacin por separado necesita pasar las variables de instalacin correctas en la lnea de argumentos make install. Estas variables son LIBDIR, HEADERDIR and ODBCINST.
294
Sustituyendo POSTGRESDIR en la lnea de argumentos de make se originar que LIBDIR y HEADERDIR puedan ser ubicados en el nuevo directorio que usted especique. ODBCINST es independiente de POSTGRESDIR. Aqu es donde usted podran especicar varios destinos explcitamente:
% make BINDIR=bindir LIBDIR=libdir HEADERDIR=headerdir install
(despus de haber usado ./congure y make) tendr como consecuencia que las bibliotecas y cheros de cabecera sean instalados en los directorios /opt/psqlodbc/lib y /opt/psqlodbc/include/iodbc respectivamente. El comando
% make POSTGRESDIR=/opt/psqlodbc HEADERDIR=/usr/local install
ocasionar que las bibliotecas sean instaladas en /opt/psqlodbc/lib y los cheros de cabecera en /usr/local/include/iodbc. Si esto no funciona como se espera por favor contacte con los mantenedores.
psqlODBC. El chero usa convenciones tpicas de los archivos de Registro de Windows, pero a pesar de esta restriccin puede hacerse funcionar. El chero .odbc.ini tiene tres secciones requeridas. La primera es [ODBC Data Sources] la cual es una lista de nombres arbitrarios y descripciones para cada base de datos a la que desee acceder. La segunda seccin es la denominada Data Source Specication y existir una de estas secciones por cada base de datos. Cada seccin
295
debe ser etiquetada con el nombre dado en [ODBC Data Sources] y debe contener las siguientes entradas:
Driver = POSTGRESDIR/lib/libpsqlodbc.so Database=DatabaseName Servername=localhost Port=5432
Sugerencia: Recuerde que el nombre de bases de datos Postgres es por lo general una palabra sencilla, sin nombres de trayectoria ni otras cosas. El servidor Postgres gestiona el acceso actual a la base de datos, y slo necesita especicar el nombre desde el cliente.
Se pueden insertar otras entradas para controlar el formato de visualizacin. La tercera seccin necesaria es [ODBC] la cual debe contener la palabra clave InstallDir adems de otras opciones.
He aqu un chero .odbc.ini de ejemplo, que muestra la informacin de acceso para tres bases de datos:
[ODBC Data Sources] DataEntry = Read/Write Database QueryOnly = Read-only Database Test = Debugging Database Default = Postgres Stripped [DataEntry] ReadOnly = 0 Servername = localhost Database = Sales [QueryOnly] ReadOnly = 1 Servername = localhost Database = Sales
296
[Test] Debug = 1 CommLog = 1 ReadOnly = 0 Servername = localhost Username = tgl Password = "no$way" Port = 5432 Database = test [Default] Servername = localhost Database = tgl Driver = /opt/postgres/current/lib/libpsqlodbc.so [ODBC] InstallDir = /opt/applix/axdata/axshlib
19.5. ApplixWare
19.5.1. Conguration
ApplixWare debe ser congurado correctamente para que pueda realizarse con l el acceso a los drivers ODBC de Postgres. Habilitando el acceso a bases de datos con ApplixWare Estas instrucciones son para la versin 4.4.1 de ApplixWare en Linux. Vase el libro on-line Administracin de Sistemas Linux para informacin ms detallada.
297
1.
Debe modicar el chero axnet.cnf para que elfodbc pueda encontrar la biblioteca compartida libodbc.so (el administrador de dispositivos ODBC). Esta biblioteca viene incluida con la distribucin de Applixware, pero el chero axnet.cnf necesita modicarse para que apunte a la ubicacin correcta. Como root, edite el chero applixroot/applix/axdata/axnet.cnf. a. Al nal del chero axnet.cnf, y busque la lnea que comienza con
#libFor elfodbc /ax/...
b.
lo cual le dir a elfodbc que busque en este directorio para la librera de soporte ODBC. Si ha instalado Applix en cualquier otro sitio, cambie la trayectoria en consecuencia. 2. Cree el chero.odbc.ini como se describi anteriormente. Tambin puede querer aadir el indicador
TextAsLongVarchar=0
a la porcin especca de bases de datos de .odbc.ini para que los campos de texto no sean mostrados como **BLOB**. Probando las conexiones ODBC de ApplixWare 1. 2. Ejecute Applix Data Seleccione la base de datos Postgres de su inters. a. b. Seleccione Query->Choose Server. Seleccione ODBC, y haga click en Browse. La base de datos que congur en el chero .odbc.ini debera mostrarse. Asegrese de que Host: field est vaco (si no es as, axnet intentar contactar con axnet en otra mquina para buscar la base de datos).
298
c. d.
Seleccione la base de datos en la caja de dilogo que ha sido lanzada por Browse, entonces haga click en OK. Introduzca el nombre de usuario y contrasea en la caja de dilogo de identicacin, y haba click en OK.
Debera ver Starting elfodbc server en la esquina inferior izquierda de la ventana de datos. Si aparece una ventana de error, vea la seccin de depuracin de abajo. 3. 4. El mensaje Ready aparecer en la esquina inferior inzquierda de la ventana de datos. Esto indica que a partir de ahora se pueden realizar consultas. Seleccione una tabla desde Query->Choose tables, y entonces seleccione Query->Query para acceder a la base de datos. Las primeras 50 las de la tabla ms o menos deberan aparecer.
los ajustes. Servidor: Tubera rota El proceso del driver ha terminado debido a algn problem. Puede que no tenga una versin actualizada del paquete ODBC de Postgres .
299
setuid a 256: fallo al lanzar la pasarela La versin de septiembre de ApplixWare v4.4.1 (la primera versin con soporte ocial de ODBC bajo Linux) presenta problemas con nombres de usuario que exceden los ocho (8) caracteres de longitud. Descripcin del problema contribuida por Steve Campbell (mailto:scampbell@lear.com).
El sistema de seguridad del programa axnet parece un poco sospechoso. axnet hace cosas en nombre del usuario y en un sistema de mltiples usuarios de verdad debera ejecutarse con seguridad de root (de este modo puede leer/escribir en cada directorio de usuario). Debera dudar al recomendar esto, sin embargo, ya que no tengo idea de qu tipo de hoyos de seguridad provoca esto.
shows
cary cary 10432 27883 0.0 2.6 1740 0.9 31.0 12692 392 4596 ? ? S S Oct 9 10:24 0:00 axnet 0:04 axmain
300
Entonces ejecute
strace -f -s 1024 -p 10432
3.
Por ejemplo, despues de obtener el mensaje Cannot launch gateway on server, ejecuto strace en axnet y obtengo
[pid 27947] open("/usr/lib/libodbc.so", O_RDONLY) = -1 ENOENT (No existe el fichero o directorio) [pid 27947] open("/lib/libodbc.so", O_RDONLY) = -1 ENOENT (No existe el fichero o directorio) [pid 27947] write(2, "/usr2/applix/axdata/elfodbc: no puedo cargar la biblioteca libodbc.so\n", 61) = -1 EIO (I/O error)
As que lo que ocurre es que elfodbc de applix est buscando libodbc.so, pero no puede encontrarlo. Por eso es por lo que axnet.cnf necesita cambiarse.
301
Modicando la Demo ApplixWare 1. 2. Copie /opt/applix/axdata/eng/Demos/sqldemo.am a un directorio local. Edite esta copia local de sqldemo.am: a. b. 3. 4. 5. 6. 7. 8. 9. Busque null_clause = "NULL" Cmbielo a null_clause = ""
Inicie Applix Macro Editor. Abra el chero sqldemo.am desde el Macro Editor. Seleccione File->Compile and Save. Salga del Macro Editor. Inicie Applix Data. Seleccione *->Run Macro Introduzca el valor sqldemo, entonces haga click en OK. Debera ver el progreso en la lnea de estado en la ventana de datos (en la esquina inferior izquierda).
302
Atencin
Deber tener cuidado con las protecciones de chero en cualquier chero que contenga informacin de nombres de usuario y contraseas.
303
JDBC is a core API of Java 1.1 and later. It provides a standard set of interfaces to SQL-compliant databases. Postgres provides a type 4 JDBC Driver. Type 4 indicates that the driver is written in Pure Java, and communicates in the databases own network protocol. Because of this, the driver is platform independent. Once compiled, the driver can be used on any platform.
Upon completion, you will nd the archive postgresql.jar in the current directory. This is the JDBC driver.
304
Nota: You must use make, not javac, as the driver uses some dynamic loading techniques for performance reasons, and javac cannot cope. The Makefile will generate the jar archive.
20.1.2.1. Example
I have an application that uses the JDBC driver to access a large database containing astronomical objects. I have the application and the jdbc driver installed in the /usr/local/lib directory, and the java jdk installed in /usr/local/jdk1.1.6. To run the application, I would use:
305
Also, the pg_hba.conf le must be congured. Its located in the PGDATA directory. In a default installation, this le permits access only by Unix domain sockets. For the JDBC driver to connect to the same localhost, you need to add something like:
host all 127.0.0.1 255.255.255.255 password
Here access to all databases are possible from the local machine with JDBC. The JDBC Driver supports trust, ident, password and crypt authentication methods.
Importante: Do not import the postgresql package. If you do, your source will not compile, as javac will get confused.
306
This will load the driver, and while loading, the driver will automatically register itself with JDBC. Note: The forName() method can throw a ClassNotFoundException, so you will need to catch it if the driver is not available. This is the most common method to use, but restricts your code to use just Postgres. If your code may access another database in the future, and you dont use our extensions, then the second method is advisable. The second method passes the driver as a parameter to the JVM as it starts, using the -D argument. Example:
% java -Djdbc.drivers=postgresql.Driver example.ImageViewer
In this example, the JVM will attempt to load the driver as part of its initialisation. Once done, the ImageViewer is started. Now, this method is the better one to use because it allows your code to be used with other databases, without recompiling the code. The only thing that would also change is the URL, which is covered next. One last thing. When your code then tries to open a Connection, and you get a No driver available SQLException being thrown, this is probably caused by the driver not being in the classpath, or the value in the parameter not being correct.
307
where: host The hostname of the server. Defaults to "localhost". port The port number the server is listening on. Defaults to the Postgres standard port number (5432). database The database name.
To connect, you need to get a Connection instance from JDBC. To do this, you would use the DriverManager.getConnection() method:
Connection db = DriverManager.getConnection(url,user,pwd);
308
You can use a Statement instance as many times as you want. You could create one as soon as you open the connection, and use it for the connections lifetime. You have to remember that only one ResultSet can exist per Statement. If you need to perform a query while processing a ResultSet, you can simply create and use another Statement. If you are using Threads, and several are using the database, you must use a separate Statement for each thread. Refer to the sections covering Threads and Servlets later in this document if you are thinking of using them, as it covers some important points.
Before reading any values, you must call next(). This returns true if there is a result, but more importantly, it prepares the row for processing.
309
Under the JDBC spec, you should access a eld only once. Its safest to stick to this rule, although at the current time, the Postgres driver will allow you to access a eld as many times as you want. You must close a ResultSet by calling close() once you have nished with it. Once you request another query with the Statement used to create a ResultSet, the currently open instance is closed.
An example is as follows:
Statement st = db.createStatement(); ResultSet rs = st.executeQuery("select * from mytable"); while(rs.next()) { System.out.print("Column 1 returned "); System.out.println(rs.getString(1)); } rs.close(); st.close();
310
Now, there are two methods of using Large Objects. The rst is the standard JDBC way, and is documented here. The other, uses our own extension to the api, which presents the libpq large object API to Java, providing even better access to large objects than the standard. Internally, the driver uses the extension to provide large object support.
311
In JDBC, the standard way to access them is using the getBinaryStream() method in ResultSet, and setBinaryStream() method in PreparedStatement. These methods make the large object appear as a Java stream, allowing you to use the java.io package, and others, to manipulate the object. For example, suppose you have a table containing the le name of an image, and a large object containing that image:
create table images (imgname name,imgoid oid);
Now in this example, setBinaryStream transfers a set number of bytes from a stream into a large object, and stores the OID into the eld holding a reference to it. Retrieving an image is even easier (Im using PreparedStatement here, but Statement can equally be used):
PreparedStatement ps = con.prepareStatement("select oid from images where name=?"); ps.setString(1,"myimage.gif"); ResultSet rs = ps.executeQuery(); if(rs!=null) {
312
while(rs.next()) { InputStream is = rs.getBinaryInputStream(1); // use the stream in some way here is.close(); } rs.close(); } ps.close();
Now here you can see where the Large Object is retrieved as an InputStream. Youll also notice that we close the stream before processing the next row in the result. This is part of the JDBC Specication, which states that any InputStream returned is closed when ResultSet.next() or ResultSet.close() is called.
313
// later on Fastpath fp = ((postgresql.Connection)db).getFastpathAPI(); Class postgresql.Connection java.lang.Object | +---postgresql.Connection public class Connection extends Object implements Connection These are the extra methods used to gain access to our extensions. I have not listed the methods defined by java.sql.Connection. public Fastpath getFastpathAPI() throws SQLException This returns the Fastpath API for the current connection. NOTE: This is not part of JDBC, but allows access to functions on the postgresql backend itself. It is primarily used by the LargeObject API The best way to use this is as follows: import postgresql.fastpath.*; ... Fastpath fp = ((postgresql.Connection)myconn).getFastpathAPI(); where myconn is an open Connection to postgresql. Returns: Fastpath object allowing access to functions on the postgresql backend. Throws: SQLException by Fastpath when initialising for first time
314
public LargeObjectManager getLargeObjectAPI() throws SQLException This returns the LargeObject API for the current connection. NOTE: This is not part of JDBC, but allows access to functions on the postgresql backend itself. The best way to use this is as follows: import postgresql.largeobject.*; ... LargeObjectManager lo = ((postgresql.Connection)myconn).getLargeObjectAPI(); where myconn is an open Connection to postgresql. Returns: LargeObject object that implements the API Throws: SQLException by LargeObject when initialising for first time public void addDataType(String type, String name) This allows client code to add a handler for one of postgresqls more unique data types. Normally, a data type not known by the driver is returned by ResultSet.getObject() as a PGobject instance. This method allows you to write a class that extends PGobject, and tell the driver the type name, and class name to use. The down side to this, is that you must call this method each time a connection is made.
315
NOTE: This is not part of JDBC, but an extension. The best way to use this is as follows: ... ((postgresql.Connection)myconn).addDataType("mytype","my.class.name"); ... where myconn is an open Connection to postgresql. The handling class must extend postgresql.util.PGobject See Also: PGobject Fastpath Fastpath is an API that exists within the libpq C interface, and allows a client machine to execute a function on the database backend. Most client code will not need to use this method, but its provided because the Large Object API uses it. To use, you need to import the postgresql.fastpath package, using the line: import postgresql.fastpath.*; Then, in your code, you need to get a FastPath object: Fastpath fp = ((postgresql.Connection)conn).getFastpathAPI(); This will return an instance associated with the database connection that you can use to issue commands. The casing of Connection to postgresql.Connection is required, as the getFastpathAPI() is one of our own methods, not JDBCs.
316
Once you have a Fastpath instance, you can use the fastpath() methods to execute a backend function. Class postgresql.fastpath.Fastpath java.lang.Object | +---postgresql.fastpath.Fastpath public class Fastpath extends Object This class implements the Fastpath api. This is a means of executing functions imbeded in the postgresql backend from within a java application. It is based around the file src/interfaces/libpq/fe-exec.c See Also: FastpathFastpathArg, LargeObject Methods public Object fastpath(int fnid, boolean resulttype, FastpathArg args[]) throws SQLException Send a function call to the PostgreSQL backend Parameters: fnid - Function id resulttype - True if the result is an integer, false for other results args - FastpathArguments to pass to fastpath
317
Returns: null if no data, Integer if an integer result, or byte[] otherwise Throws: SQLException if a database-access error occurs. public Object fastpath(String name, boolean resulttype, FastpathArg args[]) throws SQLException Send a function call to the PostgreSQL backend by name. Note: the mapping for the procedure name to function id needs to exist, usually to an earlier call to addfunction(). This is the prefered method to call, as function ids can/may change between versions of the backend. For an example of how this works, refer to postgresql.LargeObject Parameters: name - Function name resulttype - True if the result is an integer, false for other results args - FastpathArguments to pass to fastpath Returns: null if no data, Integer if an integer result, or byte[] otherwise Throws: SQLException if name is unknown or if a database-access error occurs.
318
See Also: LargeObject public int getInteger(String name, FastpathArg args[]) throws SQLException This convenience method assumes that the return value is an Integer Parameters: name - Function name args - Function arguments Returns: integer result Throws: SQLException if a database-access error occurs or no result public byte[] getData(String name, FastpathArg args[]) throws SQLException This convenience method assumes that the return value is binary data Parameters: name - Function name args - Function arguments Returns: byte[] array containing result Throws: SQLException if a database-access error occurs or no result public void addFunction(String name,
319
int fnid) This adds a function to our lookup table. User code should use the addFunctions method, which is based upon a query, rather than hard coding the oid. The oid for a function is not guaranteed to remain static, even on different servers of the same version. Parameters: name - Function name fnid - Function id public void addFunctions(ResultSet rs) throws SQLException This takes a ResultSet containing two columns. Column 1 contains the function name, Column 2 the oid. It reads the entire ResultSet, loading the values into the function table. REMEMBER to close() the resultset after calling this!! Implementation note about function name lookups: PostgreSQL stores the function ids and their corresponding names in the pg_proc table. To speed things up locally, instead of querying each function from that table when required, a Hashtable is used. Also, only the functions required are entered into this table, keeping connection times as fast as possible. The postgresql.LargeObject class performs a query upon its startup, and passes the returned ResultSet to the addFunctions() method here. Once this has been done, the LargeObject api refers to the functions by name.
320
Dont think that manually converting them to the oids will work. Ok, they will for now, but they can change during development (there was some discussion about this for V7.0), so this is implemented to prevent any unwarranted headaches in the future. Parameters: rs - ResultSet Throws: SQLException if a database-access error occurs. See Also: LargeObjectManager public int getID(String name) throws SQLException This returns the function id associated by its name If addFunction() or addFunctions() have not been called for this name, then an SQLException is thrown. Parameters: name - Function name to lookup Returns: Function ID for fastpath call Throws: SQLException is function is unknown. Class postgresql.fastpath.FastpathArg java.lang.Object | +---postgresql.fastpath.FastpathArg
321
public class FastpathArg extends Object Each fastpath call requires an array of arguments, the number and type dependent on the function being called. This class implements methods needed to provide this capability. For an example on how to use this, refer to the postgresql.largeobject package See Also: Fastpath, LargeObjectManager, LargeObject Constructors public FastpathArg(int value) Constructs an argument that consists of an integer value Parameters: value - int value to set public FastpathArg(byte bytes[]) Constructs an argument that consists of an array of bytes Parameters: bytes - array to store public FastpathArg(byte buf[], int off, int len) Constructs an argument that consists of part of a byte array Parameters:
322
buf - source array off - offset within array len - length of data to include public FastpathArg(String s) Constructs an argument that consists of a String. Parameters: s - String to store Geometric Data Types PostgreSQL has a set of datatypes that can store geometric features into a table. These range from single points, lines, and polygons. We support these types in Java with the postgresql.geometric package. It contains classes that extend the postgresql.util.PGobject class. Refer to that class for details on how to implement your own data type handlers. Class postgresql.geometric.PGbox java.lang.Object | +---postgresql.util.PGobject | +---postgresql.geometric.PGbox public class PGbox extends PGobject implements Serializable, Cloneable This represents the box datatype within postgresql. Variables
323
public PGpoint point[] These are the two corner points of the box. Constructors public PGbox(double double double double Parameters: x1 y1 x2 y2 x1, y1, x2, y2)
public PGbox(PGpoint p1, PGpoint p2) Parameters: p1 - first point p2 - second point public PGbox(String s) throws SQLException Parameters: s - Box definition in PostgreSQL syntax Throws: SQLException if definition is invalid public PGbox() Required constructor Methods
324
public void setValue(String value) throws SQLException This method sets the value of this object. It should be overidden, but still called by subclasses. Parameters: value - a string representation of the value of the object Throws: SQLException thrown if value is invalid for this type Overrides: setValue in class PGobject public boolean equals(Object obj) Parameters: obj - Object to compare with Returns: true if the two boxes are identical Overrides: equals in class PGobject public Object clone() This must be overidden to allow the object to be cloned Overrides: clone in class PGobject public String getValue() Returns: the PGbox in the syntax expected by postgresql
325
Overrides: getValue in class PGobject Class postgresql.geometric.PGcircle java.lang.Object | +---postgresql.util.PGobject | +---postgresql.geometric.PGcircle public class PGcircle extends PGobject implements Serializable, Cloneable This represents postgresqls circle datatype, consisting of a point and a radius Variables public PGpoint center This is the centre point public double radius This is the radius Constructors public PGcircle(double x, double y, double r) Parameters: x - coordinate of centre y - coordinate of centre
326
r - radius of circle public PGcircle(PGpoint c, double r) Parameters: c - PGpoint describing the circles centre r - radius of circle public PGcircle(String s) throws SQLException Parameters: s - definition of the circle in PostgreSQLs syntax. Throws: SQLException on conversion failure public PGcircle() This constructor is used by the driver. Methods public void setValue(String s) throws SQLException Parameters: s - definition of the circle in PostgreSQLs syntax. Throws: SQLException on conversion failure Overrides: setValue in class PGobject public boolean equals(Object obj) Parameters:
327
obj - Object to compare with Returns: true if the two boxes are identical Overrides: equals in class PGobject public Object clone() This must be overidden to allow the object to be cloned Overrides: clone in class PGobject public String getValue() Returns: the PGcircle in the syntax expected by postgresql Overrides: getValue in class PGobject Class postgresql.geometric.PGline java.lang.Object | +---postgresql.util.PGobject | +---postgresql.geometric.PGline public class PGline extends PGobject implements Serializable, Cloneable This implements a line consisting of two points. Currently line is not yet implemented in the backend, but this class ensures that when
328
its done were ready for it. Variables public PGpoint point[] These are the two points. Constructors public PGline(double double double double Parameters: x1 y1 x2 y2 x1, y1, x2, y2)
public PGline(PGpoint p1, PGpoint p2) Parameters: p1 - first point p2 - second point public PGline(String s) throws SQLException Parameters: s - definition of the circle in PostgreSQLs syntax. Throws: SQLException on conversion failure public PGline()
329
reuired by the driver Methods public void setValue(String s) throws SQLException Parameters: s - Definition of the line segment in PostgreSQLs syntax Throws: SQLException on conversion failure Overrides: setValue in class PGobject public boolean equals(Object obj) Parameters: obj - Object to compare with Returns: true if the two boxes are identical Overrides: equals in class PGobject public Object clone() This must be overidden to allow the object to be cloned Overrides: clone in class PGobject public String getValue()
330
Returns: the PGline in the syntax expected by postgresql Overrides: getValue in class PGobject Class postgresql.geometric.PGlseg java.lang.Object | +---postgresql.util.PGobject | +---postgresql.geometric.PGlseg public class PGlseg extends PGobject implements Serializable, Cloneable This implements a lseg (line segment) consisting of two points Variables public PGpoint point[] These are the two points. Constructors public PGlseg(double double double double Parameters: x1 - coordinate for first point y1 - coordinate for first point x2 - coordinate for second point x1, y1, x2, y2)
331
y2 - coordinate for second point public PGlseg(PGpoint p1, PGpoint p2) Parameters: p1 - first point p2 - second point public PGlseg(String s) throws SQLException Parameters: s - definition of the circle in PostgreSQLs syntax. Throws: SQLException on conversion failure public PGlseg() reuired by the driver Methods public void setValue(String s) throws SQLException Parameters: s - Definition of the line segment in PostgreSQLs syntax Throws: SQLException on conversion failure Overrides: setValue in class PGobject public boolean equals(Object obj)
332
Parameters: obj - Object to compare with Returns: true if the two boxes are identical Overrides: equals in class PGobject public Object clone() This must be overidden to allow the object to be cloned Overrides: clone in class PGobject public String getValue() Returns: the PGlseg in the syntax expected by postgresql Overrides: getValue in class PGobject Class postgresql.geometric.PGpath java.lang.Object | +---postgresql.util.PGobject | +---postgresql.geometric.PGpath public class PGpath extends PGobject implements Serializable, Cloneable This implements a path (a multiple segmented line, which may be closed)
333
Variables public boolean open True if the path is open, false if closed public PGpoint points[] The points defining this path Constructors public PGpath(PGpoint points[], boolean open) Parameters: points - the PGpoints that define the path open - True if the path is open, false if closed public PGpath() Required by the driver public PGpath(String s) throws SQLException Parameters: s - definition of the circle in PostgreSQLs syntax. Throws: SQLException on conversion failure Methods public void setValue(String s) throws SQLException Parameters:
334
s - Definition of the path in PostgreSQLs syntax Throws: SQLException on conversion failure Overrides: setValue in class PGobject public boolean equals(Object obj) Parameters: obj - Object to compare with Returns: true if the two boxes are identical Overrides: equals in class PGobject public Object clone() This must be overidden to allow the object to be cloned Overrides: clone in class PGobject public String getValue() This returns the polygon in the syntax expected by postgresql Overrides: getValue in class PGobject public boolean isOpen() This returns true if the path is open
335
public boolean isClosed() This returns true if the path is closed public void closePath() Marks the path as closed public void openPath() Marks the path as open Class postgresql.geometric.PGpoint java.lang.Object | +---postgresql.util.PGobject | +---postgresql.geometric.PGpoint public class PGpoint extends PGobject implements Serializable, Cloneable This implements a version of java.awt.Point, except it uses double to represent the coordinates. It maps to the point datatype in postgresql. Variables public double x The X coordinate of the point public double y
336
The Y coordinate of the point Constructors public PGpoint(double x, double y) Parameters: x - coordinate y - coordinate public PGpoint(String value) throws SQLException This is called mainly from the other geometric types, when a point is imbeded within their definition. Parameters: value - Definition of this point in PostgreSQLs syntax public PGpoint() Required by the driver Methods public void setValue(String s) throws SQLException Parameters: s - Definition of this point in PostgreSQLs syntax Throws: SQLException on conversion failure Overrides: setValue in class PGobject
337
public boolean equals(Object obj) Parameters: obj - Object to compare with Returns: true if the two boxes are identical Overrides: equals in class PGobject public Object clone() This must be overidden to allow the object to be cloned Overrides: clone in class PGobject public String getValue() Returns: the PGpoint in the syntax expected by postgresql Overrides: getValue in class PGobject public void translate(int x, int y) Translate the point with the supplied amount. Parameters: x - integer amount to add on the x axis y - integer amount to add on the y axis public void translate(double x, double y)
338
Translate the point with the supplied amount. Parameters: x - double amount to add on the x axis y - double amount to add on the y axis public void move(int x, int y) Moves the point to the supplied coordinates. Parameters: x - integer coordinate y - integer coordinate public void move(double x, double y) Moves the point to the supplied coordinates. Parameters: x - double coordinate y - double coordinate public void setLocation(int x, int y) Moves the point to the supplied coordinates. refer to java.awt.Point for description of this Parameters: x - integer coordinate y - integer coordinate See Also: Point
339
public void setLocation(Point p) Moves the point to the supplied java.awt.Point refer to java.awt.Point for description of this Parameters: p - Point to move to See Also: Point Class postgresql.geometric.PGpolygon java.lang.Object | +---postgresql.util.PGobject | +---postgresql.geometric.PGpolygon public class PGpolygon extends PGobject implements Serializable, Cloneable This implements the polygon datatype within PostgreSQL. Variables public PGpoint points[] The points defining the polygon Constructors public PGpolygon(PGpoint points[]) Creates a polygon using an array of PGpoints
340
Parameters: points - the points defining the polygon public PGpolygon(String s) throws SQLException Parameters: s - definition of the circle in PostgreSQLs syntax. Throws: SQLException on conversion failure public PGpolygon() Required by the driver Methods public void setValue(String s) throws SQLException Parameters: s - Definition of the polygon in PostgreSQLs syntax Throws: SQLException on conversion failure Overrides: setValue in class PGobject public boolean equals(Object obj) Parameters: obj - Object to compare with Returns: true if the two boxes are identical Overrides:
341
equals in class PGobject public Object clone() This must be overidden to allow the object to be cloned Overrides: clone in class PGobject public String getValue() Returns: the PGpolygon in the syntax expected by postgresql Overrides: getValue in class PGobject Large Objects Large objects are supported in the standard JDBC specification. However, that interface is limited, and the api provided by PostgreSQL allows for random access to the objects contents, as if it was a local file. The postgresql.largeobject package profides to Java the libpq C interfaces large object API. It consists of two classes, LargeObjectManager, which deals with creating, opening and deleting large obejects, and LargeObject which deals with an individual object. Class postgresql.largeobject.LargeObject java.lang.Object | +---postgresql.largeobject.LargeObject public class LargeObject extends Object
342
This class implements the large object interface to postgresql. It provides the basic methods required to run the interface, plus a pair of methods that provide InputStream and OutputStream classes for this object. Normally, client code would use the getAsciiStream, getBinaryStream, or getUnicodeStream methods in ResultSet, or setAsciiStream, setBinaryStream, or setUnicodeStream methods in PreparedStatement to access Large Objects. However, sometimes lower level access to Large Objects are required, that are not supported by the JDBC specification. Refer to postgresql.largeobject.LargeObjectManager on how to gain access to a Large Object, or how to create one. See Also: LargeObjectManager Variables public static final int SEEK_SET Indicates a seek from the begining of a file public static final int SEEK_CUR Indicates a seek from the current position public static final int SEEK_END Indicates a seek from the end of a file Methods public int getOID()
343
Returns: the OID of this LargeObject public void close() throws SQLException This method closes the object. You must not call methods in this object after this is called. Throws: SQLException if a database-access error occurs. public byte[] read(int len) throws SQLException Reads some data from the object, and return as a byte[] array Parameters: len - number of bytes to read Returns: byte[] array containing data read Throws: SQLException if a database-access error occurs. public void read(byte buf[], int off, int len) throws SQLException Reads some data from the object into an existing array Parameters: buf - destination array off - offset within array len - number of bytes to read
344
Throws: SQLException if a database-access error occurs. public void write(byte buf[]) throws SQLException Writes an array to the object
Parameters: buf - array to write Throws: SQLException if a database-access error occurs. public void write(byte buf[], int off, int len) throws SQLException Writes some data from an array to the object Parameters: buf - destination array off - offset within array len - number of bytes to write Throws: SQLException if a database-access error occurs. public void seek(int pos, int ref) throws SQLException Sets the current position within the object. This is similar to the fseek() call in the standard C library.It allows you to have random access to the large object. Parameters:
345
pos - position within object ref - Either SEEK_SET, SEEK_CUR or SEEK_END Throws: SQLException if a database-access error occurs. public void seek(int pos) throws SQLException Sets the current position within the object. This is similar to the fseek() call in the standard C library.It allows you to have random access to the large object. Parameters: pos - position within object from begining Throws: SQLException if a database-access error occurs. public int tell() throws SQLException Returns: the current position within the object Throws: SQLException if a database-access error occurs. public int size() throws SQLException This method is inefficient, as the only way to find out the size of the object is to seek to the end, record the current position, then return to the original position. A better method will be found in the future. Returns: the size of the large object
346
Throws: SQLException if a database-access error occurs. public InputStream getInputStream() throws SQLException Returns an InputStream from this object. This InputStream can then be used in any method that requires an InputStream. Throws: SQLException if a database-access error occurs. public OutputStream getOutputStream() throws SQLException Returns an OutputStream to this object This OutputStream can then be used in any method that requires an OutputStream. Throws: SQLException if a database-access error occurs. Class postgresql.largeobject.LargeObjectManager java.lang.Object | +---postgresql.largeobject.LargeObjectManager public class LargeObjectManager extends Object This class implements the large object interface to postgresql. It provides methods that allow client code to create, open and delete large objects from the database. When opening an object, an instance of postgresql.largeobject.LargeObject is returned, and its methods then allow access to the object.
347
This class can only be created by postgresql.Connection To get access to this class, use the following segment of code: import postgresql.largeobject.*; Connection conn; LargeObjectManager lobj; ... code that opens a connection ... lobj = ((postgresql.Connection)myconn).getLargeObjectAPI(); Normally, client code would use the getAsciiStream, getBinaryStream, or getUnicodeStream methods in ResultSet, or setAsciiStream, setBinaryStream, or setUnicodeStream methods in PreparedStatement to access Large Objects. However, sometimes lower level access to Large Objects are required, that are not supported by the JDBC specification. Refer to postgresql.largeobject.LargeObject on how to manipulate the contents of a Large Object. See Also: LargeObject Variables public static final int WRITE This mode indicates we want to write to an object public static final int READ This mode indicates we want to read an object public static final int READWRITE
348
This mode is the default. It indicates we want read and write access to a large object Methods public LargeObject open(int oid) throws SQLException This opens an existing large object, based on its OID. This method assumes that READ and WRITE access is required (the default). Parameters: oid - of large object Returns: LargeObject instance providing access to the object Throws: SQLException on error public LargeObject open(int oid, int mode) throws SQLException This opens an existing large object, based on its OID Parameters: oid - of large object mode - mode of open Returns: LargeObject instance providing access to the object Throws: SQLException on error public int create() throws SQLException
349
This creates a large object, returning its OID. It defaults to READWRITE for the new objects attributes. Returns: oid of new object Throws: SQLException on error public int create(int mode) throws SQLException This creates a large object, returning its OID Parameters: mode - a bitmask describing different attributes of the new object Returns: oid of new object Throws: SQLException on error public void delete(int oid) throws SQLException This deletes a large object. Parameters: oid - describing object to delete Throws: SQLException on error public void unlink(int oid) throws SQLException
350
This deletes a large object. It is identical to the delete method, and is supplied as the C API uses unlink. Parameters: oid - describing object to delete Throws: SQLException on error Object Serialisation PostgreSQL is not a normal SQL Database. It is far more extensible than most other databases, and does support Object Oriented features that are unique to it. One of the consequences of this, is that you can have one table refer to a row in another table. For example: test=> create table users (username name,fullname text); CREATE test=> create table server (servername name,adminuser users); CREATE test=> insert into users values (peter,Peter Mount); INSERT 2610132 1 test=> insert into server values (maidast,2610132::users); INSERT 2610133 1 test=> select * from users; username|fullname -----+--------peter |Peter Mount (1 row) test=> select * from server; servername|adminuser -------+-----maidast | 2610132
351
(1 row) Ok, the above example shows that we can use a table name as a field, and the rows oid value is stored in that field. What does this have to do with Java? In Java, you can store an object to a Stream as long as its class implements the java.io.Serializable interface. This process, known as Object Serialization, can be used to store complex objects into the database. Now, under JDBC, you would have to use a LargeObject to store them. However, you cannot perform queries on those objects. What the postgresql.util.Serialize class does, is provide a means of storing an object as a table, and to retrieve that object from a table. In most cases, you would not need to access this class direct, but you would use the PreparedStatement.setObject() and ResultSet.getObject() methods. Those methods will check the objects class name against the tables in the database. If a match is found, it assumes that the object is a Serialized object, and retrieves it from that table. As it does so, if the object contains other serialized objects, then it recurses down the tree. Sounds complicated? In fact, its simpler than what I wrote - its just difficult to explain. The only time you would access this class, is to use the create() methods. These are not used by the driver, but issue one or more "create table" statements to the database, based on a Java Object or Class that you want to serialize. Oh, one last thing. If your object contains a line like: public int oid;
352
then, when the object is retrieved from the table, it is set to the oid within the table. Then, if the object is modified, and reserialized, the existing entry is updated. If the oid variable is not present, then when the object is serialized, it is always inserted into the table, and any existing entry in the table is preserved. Setting oid to 0 before serialization, will also cause the object to be inserted. This enables an object to be duplicated in the database. Class postgresql.util.Serialize java.lang.Object | +---postgresql.util.Serialize public class Serialize extends Object This class uses PostgreSQLs object oriented features to store Java Objects. It does this by mapping a Java Class name to a table in the database. Each entry in this new table then represents a Serialized instance of this class. As each entry has an OID (Object IDentifier), this OID can be included in another table. This is too complex to show here, and will be documented in the main documents in more detail. Constructors public Serialize(Connection c, String type) throws SQLException This creates an instance that can be used to serialize ordeserialize a Java object from a PostgreSQL table. Methods public Object fetch(int oid) throws SQLException
353
This fetches an object from a table, given its OID Parameters: oid - The oid of the object Returns: Object relating to oid Throws: SQLException on error public int store(Object o) throws SQLException This stores an object into a table, returning its OID. If the object has an int called OID, and it is > 0, then that value is used for the OID, and the table will be updated. If the value of OID is 0, then a new row will be created, and the value of OID will be set in the object. This enables an objects value in the database to be updateable. If the object has no int called OID, then the object is stored. However if the object is later retrieved, amended and stored again, its new state will be appended to the table, and will not overwrite the old entries. Parameters: o - Object to store (must implement Serializable) Returns: oid of stored object Throws: SQLException on error public static void create(Connection con, Object o) throws SQLException
354
This method is not used by the driver, but it creates a table, given a Serializable Java Object. It should be used before serializing any objects. Parameters: c - Connection to database o - Object to base table on Throws: SQLException on error Returns: Object relating to oid Throws: SQLException on error public int store(Object o) throws SQLException This stores an object into a table, returning its OID. If the object has an int called OID, and it is > 0, then that value is used for the OID, and the table will be updated. If the value of OID is 0, then a new row will be created, and the value of OID will be set in the object. This enables an objects value in the database to be updateable. If the object has no int called OID, then the object is stored. However if the object is later retrieved, amended and stored again, its new state will be appended to the table, and will not overwrite the old entries. Parameters: o - Object to store (must implement Serializable) Returns: oid of stored object Throws: SQLException
355
on error public static void create(Connection con, Object o) throws SQLException This method is not used by the driver, but it creates a table, given a Serializable Java Object. It should be used before serializing any objects. Parameters: c - Connection to database o - Object to base table on Throws: SQLException on error public static void create(Connection con, Class c) throws SQLException This method is not used by the driver, but it creates a table, given a Serializable Java Object. It should be used before serializing any objects. Parameters: c - Connection to database o - Class to base table on Throws: SQLException on error public static String toPostgreSQL(String name) throws SQLException This converts a Java Class name to a postgresql table, by replacing . with _ Because of this, a Class name may not have _ in the name.
356
Another limitation, is that the entire class name (including packages) cannot be longer than 31 characters (a limit forced by PostgreSQL). Parameters: name - Class name Returns: PostgreSQL table name Throws: SQLException on error public static String toClassName(String name) throws SQLException This converts a postgresql table to a Java Class name, by replacing _ with . Parameters: name - PostgreSQL table name Returns: Class name Throws: SQLException on error Utility Classes The postgresql.util package contains classes used by the internals of the main driver, and the other extensions. Class postgresql.util.PGmoney java.lang.Object | +---postgresql.util.PGobject
357
| +---postgresql.util.PGmoney public class PGmoney extends PGobject implements Serializable, Cloneable This implements a class that handles the PostgreSQL money type Variables public double val The value of the field Constructors public PGmoney(double value) Parameters: value - of field public PGmoney(String value) throws SQLException This is called mainly from the other geometric types, when a point is imbeded within their definition. Parameters: value - Definition of this point in PostgreSQLs syntax public PGmoney() Required by the driver Methods public void setValue(String s) throws SQLException
358
Parameters: s - Definition of this point in PostgreSQLs syntax Throws: SQLException on conversion failure Overrides: setValue in class PGobject public boolean equals(Object obj) Parameters: obj - Object to compare with Returns: true if the two boxes are identical Overrides: equals in class PGobject public Object clone() This must be overidden to allow the object to be cloned Overrides: clone in class PGobject public String getValue() Returns: the PGpoint in the syntax expected by postgresql Overrides: getValue in class PGobject Class postgresql.util.PGobject
359
java.lang.Object | +---postgresql.util.PGobject public class PGobject extends Object implements Serializable, Cloneable This class is used to describe data types that are unknown by JDBC Standard. A call to postgresql.Connection permits a class that extends this class to be associated with a named type. This is how the postgresql.geometric package operates. ResultSet.getObject() will return this class for any type that is not recognised on having its own handler. Because of this, any postgresql data type is supported. Constructors public PGobject() This is called by postgresql.Connection.getObject() to create the object. Methods public final void setType(String type) This method sets the type of this object. It should not be extended by subclasses, hence its final Parameters: type - a string describing the type of the object public void setValue(String value) throws SQLException
360
This method sets the value of this object. It must be overidden. Parameters: value - a string representation of the value of the object Throws: SQLException thrown if value is invalid for this type public final String getType() As this cannot change during the life of the object, its final. Returns: the type name of this object public String getValue() This must be overidden, to return the value of the object, in the form required by postgresql. Returns: the value of this object public boolean equals(Object obj) This must be overidden to allow comparisons of objects Parameters: obj - Object to compare with Returns: true if the two boxes are identical
361
Overrides: equals in class Object public Object clone() This must be overidden to allow the object to be cloned Overrides: clone in class Object public String toString() This is defined here, so user code need not overide it. Returns: the value of this object, in the syntax expected by postgresql Overrides: toString in class Object Class postgresql.util.PGtokenizer java.lang.Object | +---postgresql.util.PGtokenizer public class PGtokenizer extends Object This class is used to tokenize the text output of postgres. We could have used StringTokenizer to do this, however, we needed to handle nesting of ( ) [ ] < and > as these are used by the geometric data types. Its mainly used by the geometric classes, but is useful in parsing any output from custom data types output from postgresql.
362
See Also: PGbox, PGcircle, PGlseg, PGpath, PGpoint, PGpolygon Constructors public PGtokenizer(String string, char delim) Create a tokeniser. Parameters: string - containing tokens delim - single character to split the tokens Methods public int tokenize(String string, char delim) This resets this tokenizer with a new string and/or delimiter. Parameters: string - containing tokens delim - single character to split the tokens public int getSize() Returns: the number of tokens available public String getToken(int n) Parameters: n - Token number ( 0 ... getSize()-1 )
363
Returns: The token value public PGtokenizer tokenizeToken(int n, char delim) This returns a new tokenizer based on one of our tokens. The geometric datatypes use this to process nested tokens (usually PGpoint). Parameters: n - Token number ( 0 ... getSize()-1 ) delim - The delimiter to use Returns: A new instance of PGtokenizer based on the token public static String remove(String s, String l, String t) This removes the lead/trailing strings from a string Parameters: s - Source string l - Leading string to remove t - Trailing string to remove Returns: String without the lead/trailing strings public void remove(String l, String t) This removes the lead/trailing strings from all tokens Parameters:
364
l - Leading string to remove t - Trailing string to remove public static String removePara(String s) Removes ( and ) from the beginning and end of a string Parameters: s - String to remove from Returns: String without the ( or ) public void removePara() Removes ( and ) from the beginning and end of all tokens Returns: String without the ( or ) public static String removeBox(String s) Removes [ and ] from the beginning and end of a string Parameters: s - String to remove from Returns: String without the [ or ] public void removeBox() Removes [ and ] from the beginning and end of all tokens Returns: String without the [ or ]
365
public static String removeAngle(String s) Removes < and > from the beginning and end of a string Parameters: s - String to remove from Returns: String without the < or > public void removeAngle() Removes < and > from the beginning and end of all tokens Returns: String without the < or > Class postgresql.util.Serialize This was documented earlier under Object Serialisation. Class postgresql.util.UnixCrypt java.lang.Object | +---postgresql.util.UnixCrypt public class UnixCrypt extends Object This class provides us with the ability to encrypt passwords when sent over the network stream Contains static methods to encrypt and compare passwords with Unix encrypted passwords. See John Dumass Java Crypt page for the original source.
366
http://www.zeh.com/local/jfd/crypt.html Methods public static final String crypt(String salt, String original) Encrypt a password given the cleartext password and a "salt". Parameters: salt - A two-character string representing the salt used to iterate the encryption engine in lots of different ways. If you are generating a new encryption then this value should be randomised. original - The password to be encrypted. Returns: A string consisting of the 2-character salt followed by the encrypted password. public static final String crypt(String original) Encrypt a password given the cleartext password. This method generates a random salt using the java.util.Random class. Parameters: original - The password to be encrypted. Returns: A string consisting of the 2-character salt followed by the encrypted password. public static final boolean matches(String encryptedPassword,
367
String enteredPassword) Check that enteredPassword encrypts to encryptedPassword. Parameters: encryptedPassword - The encryptedPassword. The first two characters are assumed to be the salt. This string would be the same as one found in a Unix /etc/passwd file. enteredPassword - The password as entered by the user (or otherwise aquired). Returns: true if the password should be considered correct. Using the driver in a multi Threaded or Servlet environment A problem with many JDBC drivers, is that only one thread can use a Connection at any one time - otherwise a thread could send a query while another one is receiving results, and this would be a bad thing for the database engine. PostgreSQL 6.4, brings thread safety to the entire driver. Standard JDBC was thread safe in 6.3.x, but the Fastpath API wasnt. So, if your application uses multiple threads (which most decent ones would), then you dont have to worry about complex schemes to ensure only one uses the database at any time. If a thread attempts to use the connection while another is using it, it will wait until the other thread has finished its current operation. If its a standard SQL statement, then the operation is sending the statement, and retrieving any ResultSet (in full). If its a Fastpath call (ie: reading a block from a LargeObject), then
368
its the time to send, and retrieve that block. This is fine for applications & applets, but can cause a performance problem with servlets. With servlets, you can have a heavy load on the connection. If you have several threads performing queries, then each one will pause, which may not be what you are after. To solve this, you would be advised to create a pool of Connections. When ever a thread needs to use the database, it asks a manager class for a Connection. It hands a free connection to the thread, and marks it as busy. If a free connection is not available, it opens one. Once the thread has finished with it, it returns it to the manager, who can then either close it, or add it to the pool. The manager would also check that the connection is still alive, and remove it from the pool if its dead. So, with servlets, its up to you to use either a single connection, or a pool. The plus side for a pool is that threads will not be hit by the bottle neck caused by a single network connection. The down side, is that it increases the load on the server, as a backend is created for each Connection. Its up to you, and your applications requirements.
369
My own web site (http://www.retep.org.uk) contains updated information not included in this document, and also includes precompiled drivers for v6.4, and earlier.
370
pg.el es una interfaz a nivel de socket a Postgres para emacs (extraordinario editor de
texto). El mdulo es capaz de asignar tipos de SQL al tipo equivalente de Emacs Lisp. Actualmente no soporta ni encriptacin ni autenticacin Kerberos, ni objetos grandes (large objects). El cdigo (version 0.2) est disponible bajo la licencia GNU GPL en http://www.chez.com/emarsden/downloads/pg.el (http://www.chez.com/emarsden/downloads/pg.el) Cambios desde la ltima versin:
ahora funciona con XEmacs (probado con Emacs 19.34 y 20.2, y XEmacs 20.4) aadidas funciones para proveer metainformacin (lista de bases de datos, de tablas, de columnas) los argumentos de pg:result son ahora :keywords Resistente a MULE ms cdigo de autocomprobacin
Por favor, ntese que esta es una API de programadores, y no proporciona ninguna forma de interfaz con el usuario. Ejemplo:
(defun demo ()
371
(interactive) (let* ((conn (pg:connect "template1" "postgres" "postgres")) (res (pg:exec conn "SELECT * from scshdemo WHERE a = 42"))) (message "status is %s" (pg:result res :status)) (message "metadata is %s" (pg:result res :attributes)) (message "data is %s" (pg:result res :tuples)) (pg:disconnect conn)))
372
373
:set ts=4
Las herramientas para ver textos more y less pueden ser invocadas como
more -x4 less -x4
374
Este captulo da una visin general de la estructura interna del motor de Postgres. Tras la lectura de las siguientes secciones, usted tendr una idea de como se procesa una consulta. No espere aqu una descripcin detallada (creo que esa descripcin detallada incluyendo todas las estructuras de datos y funciones utilizadas en Postgres excedera de 1000 pginas!). Este captulo intenta ayudar en la comprensin del control general y del ujo de datos dentro del motor desde que se recibe una consulta hasta que se emiten los resultados.
2.
3.
375
aplicarle al rbol de la consulta y realiza las transformaciones que se dan en el/los cuerpo/s de la/s regla/s. Encontramos una aplicacin del sistema de reescritura en la realizacin de las vistas. Siempre que se realiza una consulta contra una vista (es decir, una tabla virtual), el sistema de reescritura reescribe la consulta del usuario en una consulta que accede a las tablas base dadas en la denicin de la vista inicial. 4. El planeador/optimizador toma el rbol de la consulta (reescrita) y crea un plan de la consulta que ser el input para el ejecutor. Hace esto creando previamente todas las posibles rutas que le conducen a un mismo resultado. Por ejemplo, si hay un ndice en una relacin que debe ser comprobada, hay dos rutas para comprobarla. Una posibilidad es un simple barrido secuencial y la otra posibilidad es utilizar el ndice. Luego se estima el coste de ejecucin de cada plan, y se elige y ejecuta el plan ms rpido. 5. El ejecutor realiza de modo recursivo el rbol del plan y recupera tuplas en la forma representada en el plan. El ejecutor hace uso del sistema de almacenamiento mientras est revisando las relaciones, realiza ordenaciones (sorts) y joins, evala cualicaciones y nalmente devuelve las tuplas derivadas.
En las siguientes secciones, cubriremos todos los pasos listados antes en ms detalle, para dar un mejor conocimiento de las estructuras de datos y de control interno de Postgres.
376
llamado postgres. Las tareas de servidor (los procesos postgres) se comunican unos con otros utilizando semforos y memoria compartida (shared memory) para asegurar la integridad de los datos a travs de los accesos concurrentes a los datos. La gura \ref{connection} ilustra la interaccin del proceso master postmaster, el proceso servidor postgres y una aplicacin cliente. El proceso cliente puede ser el interface de usuario (frontend) psql (para realizar consultas SQL interactivas) o cualquier aplicacin de usuario implementada utilizando la biblioteca libpg. Ntese que las aplicaciones implementadas utilizando ecpg (el preprocesador de SQL embebido de Postgres para C) tambin utiliza esta biblioteca. Una vez que se ha establecido una conexin, el proceso cliente puede enviar una consulta al servidor (backend). Esta consulta se transmite utilizando un texto plano, es decir, no se ha hecho una traduccin en el cliente (frontend). El servidor traduce la consulta, crea un plan de ejecucin, ejecuta el plan y remite las tuplas recuperadas al cliente a travs de la conexin establecida.
El traductor denido en gram.y y scan.l se construye utilizando las herramientas de Unix yacc y lex. El proceso de transformacin realiza modicaciones y aumentos a las estructuras de datos devueltas por el traductor.
23.3.1. Traductor
El traductor debe comprobar la cadena de caracteres de la consulta (que le llega como texto ASCII plano) para comprobar la validez de la sintaxis. Si la sintaxis es correcta, se
377
construye un rbol de traduccin y se devuelve un mensaje de error en otro caso. Para la implementacin se han utilizado las bien conocidas herramientas de Unix lex y yacc. El lector (lexer) se dene en el chero scan.l y es el responsable de reconocer los identicadores, las palabras clave de SQL, etc. Para cada palabra clave o identicador que encuentra, se genera y traslada al traductor traductor una seal. El traductor est denido en el chero gram.y y consiste en un conjunto de reglas de gramtica y acciones que sern ejecutadas cada vez que se dispara una regla. El cdigo de las acciones (que actualmente es cdigo C) se utiliza para construir el rbol de traduccin. El chero scan.l se transforma en el chero fuente C scan.c utilizando el programa lex u gram.y se transforma en gram.c utilizando yacc. Una vez se han realizado estas transformaciones, cualquier compilador C puede utilizarse para crear el traductor. No se deben nunca realizar cambio en los cheros C generados, pues sern sobreescritos la prxima vez que sean llamados lex o yacc.
Nota: Las transformaciones y compilaciones mencionadas normalmente se hacen automticamente utilizando los makele vendidos con la distribucin de los fuentes de Postgres.
Ms adelante en este mismo documento se dar una descripcin detallada de yacc o de las reglas de gramtica dadas en gram.y. Hay muchos libros y documentos relacionados con lex y yacc. Debera usted familiarizarse con yacc antes de empezar a estudiar la gramtica mostrada en gram.y, pues de otro modo no entender usted lo que est haciendo. Para un mejor conocimiento de las estructuras de datos utilizadas en Postgres para procesar una consulta utilizaremos un ejemplo para ilustrar los cambios hechos a estas estructuras de datos en cada etapa.
378
Ejemplo 23-1. Una SELECT sencilla Este ejemplo contiene la siguiente consulta sencilla que ser usada en varias descripciones y guras a lo largo de las siguientes secciones. La consulta asume que las tablas dadas en The Supplier Database ya han sido denidas.
select s.sname, se.pno from supplier s, sells se where s.sno > 2 and s.sno = se.sno;
La gura \ref{parsetree} muestra el rbol de traduccin construido por las reglas y acciones de gramtica dadas en gram.y para la consulta dada en Una SELECT sencillaEste ejemplo contiene la siguiente consulta sencilla que ser usada en varias descripciones y guras a lo largo de las siguientes secciones. La consulta asume que las tablas dadas en The Supplier Database ya han sido denidas. select s.sname, se.pno from supplier s, sells se where s.sno > 2 and s.sno = se.sno; (sin el rbol de operador para la clusula WHERE que se muestra en la gura \ref{where_clause} porque no haba espacio suciente para mostrar ambas estructuras de datos en una sola gura). El nodo superior del rbol es un nodo SelectStmt. Para cada entrada que aparece en la clusula FROM de la consulta de SQL se crea un nodo RangeVar que mantiene el nombre del alias y un puntero a un nodo RelExpr que mantiene el nombre de la relacin. Todos los nodos RangeVar estn recogidas en una lista unida al campo fromClause del nodo SelectStmt. Para cada entrada que aparece en la lista de la SELECT de la consulta de SQL se crea un nodo ResTarget que contiene un puntero a un nodo Attr. El nodo Attr contiene el nombre de la relacin de la entrada y un puntero a un nodo Value que contiene el nombre del attribute. Todos los nodos ResTarget estn reunidos en una lista que est conectada al campo targetList del nodo SelectStmt. La gura \ref{where_clause} muestra el rbol de operador construido para la clausula WHERE de la consulta de SQL dada en el ejemplo Una SELECT sencillaEste ejemplo contiene la siguiente consulta sencilla que ser usada en varias descripciones y guras a lo largo de las siguientes secciones. La consulta asume que las tablas dadas en The
379
Supplier Database ya han sido denidas. select s.sname, se.pno from supplier s, sells se where s.sno > 2 and s.sno = se.sno; que est unido al campo qual del nodo SelectStmt. El nodo superior del rbol de operador es un nodo A_Expr representando una operacin AND. Este nodo tiene dos sucesores llamados lexpr y rexpr apuntando a dos subrboles. El subrbol unido a lexpr representa la cualicacin s.sno > 2 y el unido a rexpr representa s.sno = se.sno. Para cada atributo, se ha creado un nodo Attr que contiene el nombre de la relacin y un puntero a un nodo Value que contiene el nombre del atributo. Para el termino constante que aparece en la consulta, se ha creado un nodo Const que contiene el valor.
380
de entradas de la tabla de rango creada antes. El campo varattno da la posicin del atributo dentro de la relacin. Si el nombre de un atributo no se consigue encontrar, se devuelve un error y se aborta el procesado de la consulta.
El primero trabajaba utilizando el procesado a nivel de tupla y se implementaba en el ejecutor. El sistema de reglas se disparaba cada vez que se acceda una tupla individual. Esta implementacin se retir en 1.995 cuando la ltima versin ocial del proyecto Postgres se transform en Postgres95. La segunda implementacin del sistema de reglas es una tcnica llamada reescritura de la consulta. El sistema de reescritura es un mdulo que existe entre la etapa del traductor y el planicador/optimizador. Est tcnica contina implementada.
Para informacin sobre la sintaxis y la creacin de reglas en sistema Postgres Dirjase a la Gua del Usuario de PostgreSQL.
381
Esta regla se disparar cada vez que se detecte una SELECT contra la relacin test_view. En lugar de seleccionar las tuplas de test_view, se ejecutar la instruccin SELECT dada en la parte de la accin de la regla. Tengamos la siguiente consulta de usuario contra test_view:
select sname from test_view where sname <> Smith;
Tenemos aqu una lista de los pasos realizados por el sistema de reescritura de la consulta cada vez que aparece una consulta de usuario contra test_view. (El siguiente listado es una descripcin muy informal del algoritmo nicamente para una comprensin bsica. Para una descripcin detallada dirjase a Stonebraker et al, 1989).
382
Reescritura de test_view 1. 2. 3. Toma la consulta dada por la parte de accin de la regla. Adapta la lista-objetivo para recoger el nmero y orden de los atributos dados en la consulta del usuario. Aade la cualicacin dada en la clusula WHERE de la consulta del usuario a la cualicacin de la consulta dada en la parte de la accin de la regla.
Dada la denicin de la regla anterior, la consulta del usuario ser reescrita a la siguiente forma (Ntese que la reescritura se hace en la representacin interna de la consulta del usuario devuelta por la etapa de traduccin, pero la nueva estructura de datos representar la siguiente consulta):
select s.sname from supplier s, sells se, part p where s.sno = se.sno and p.pno = se.pno and s.sname <> Smith;
23.5. Planicador/optimizador
La tarea del planicador/optimizador es crear un plan de ejecucin ptimo. Primero combina todas las posibles vas de barrer (scannear) y cruzar (join) las relaciones que aparecen en una consulta. Todas las rutas creadas conducen al mismo resultado y es el trabajo del optimizador estimar el coste de ejecutar cada una de ellas para encontrar cual es la ms econmica.
383
Cruce de iteracin anidada (nested iteration join): La relacin derecha se recorre para cada tupla encontrada en la relacin izquierda. Esta estrategia es fcil de implementar pero puede consumir mucho tiempo. Cruce de ordenacin mezclada (merge sort join): Cada relacin es ordenada por los atributos del cruce antes de iniciar el cruce mismo. Despus se mezclan las dos relaciones teniendo en cuenta que ambas relaciones estn ordenadas pro los atributos del cruce. Este modelo de cruce es ms atractivo porque cada relacin debe ser barrida slo una vez. Cruce indexado (hash join): La relacin de la derecha se indexa primero sobre sus atributos para el cruce. A continuacin, se barre la relacin izquierda, y los valores apropiados de cada tupla encontrada se utilizan como clave indexada para localizar las tuplas de la relacin derecha.
384
385
23.6. Ejecutor
El ejecutor toma el plan devuelto por el planicador/optimizador y arranca procesando el nodo superior. En el caso de nuestro ejemplo (la consulta dada en el ejemplo \ref{simple_select}), el nodo superior es un nodo Cruce Mezclado (MergeJoin). Antes de poder hacer ninguna mezcla, se deben leer dos tuplas, una de cada subplan. De este modo, el ejecutor mismo llama recursivamente a procesar los subplanes (arranca con el subplan unido al rbol izquierdo). El nuevo nodo superior (el nodo superior del subplan izquierdo) es un nodo SeqScan, y de nuevo se debe tomar una tupla antes de que el nodo mismo pueda procesarse. El ejecutor mismo llama recursivamente otra vez al subplan unido al rbol izquierdo del nodo SeqScan. El nuevo nodo superior es un nodo Sort. Como un sort se debe realizar sobre la relacin completa, el ejecutor arranca leyendo tuplas desde el subplan del nodo Sort y las ordena en una relacin temporal (en memoria o en un chero) cuando se visita por primera vez el nodo Sort. (Posteriores exmenes del nodo Sort devolvern siempre nicamente una tupla de la relacin temporalmente ordenada). Cada vez que el procesado del nodo Sort necesita de una nueva tupla, se llama de forma recursiva al ejecutor para que trate el nodo SeqScan unido como subplan. La relacin (a la que se reere internamente por el valor dado en el campo scanrelid) se recorre para encontrar la siguiente tupla. Si la tupla satisface la cualicacin dada por el rbol unido a qpqual se da por buena para su tratamiento, y en otro caso se lee la siguiente tupla hasta la primera que satisfaga la cualicacin. Si se ha procesado la ltima tupla de la relacin, se devuelve un puntero NULL. Una vez que se ha recuperado una tupla en el rbol izquierdo del Cruce Mezclado (MergeJoin), se procesa del mismo modo el rbol derecho. Si se tienen presentes ambas tuplas, el ejecutor procesa el Cruce Mezclado. Siempre que se necesita una nueva tupla de uno de los subplanes, se realiza una llamada recursiva al ejecutor para obtenerla. Si se pudo crear una tupla para cruzarla, se devuelve y se da por terminado el procesado completo de rbol del plan. Se realizan ahora los pasos descritos para cada una de las tuplas, hasta que se devuelve un puntero NULL para el procesado del nodo Cruce Mezclado, indicando que hemos terminado.
386
El chero opcional data/pg_options contiene opciones de tiempo de ejecucin utilizadas por el servidor para controlar los mensajes de seguimiento y otros parmetros ajustables de servidor. Lo que hace a este chero interesante es el hecho de que es re-leido por un servidor qeu recibe una seal SIGHUP, haciendo as posible cambiar opciones de tiempo de ejecucin sobre la marcha sin necesidad de rearrancar Postgres. Las opciones especicadas en este chero pueden ser banderas de debugging utilizadas por el paquete de seguimiento (backend/utils/misc/trace.c) o parmetros numricos que pueden ser usados por el servidor para controlar su comportamiento. Las nuevas opciones y parmetros deben ser denidos en backend/utils/misc/trace.c y backend/include/utils/trace.h. Por ejemplo, supongamos que queremos aadir mensajes de seguimiento condicional y un parmetro numrico ajustable al cdigo en el chero foo.c. Todo lo que necesitamos hacer es aadir la constante TRACE_FOO y OPT_FOO_PARAM en backend/include/utils/trace.h:
/* file trace.h */ enum pg_option_enum { ... TRACE_FOO, /* trace foo functions */ OPT_FOO_PARAM, /* foo tunable parameter */ NUM_PG_OPTIONS }; /* must be the last item of enum */
387
/* file trace.c */ static char *opt_names[] = { ... "foo", /* trace foo functions */ "fooparam" /* foo tunable parameter */ };
Las opciones se deben especicar en los dos cheros exctamente en el mismo orden. En los cheros fuente foo podemos ahora hacer referencia a las nuevas banderas con:
/* file foo.c */ #include "trace.h" #define foo_param pg_options[OPT_FOO_PARAM] int foo_function(int x, int y) { TPRINTF(TRACE_FOO, "entering foo_function, foo_param=%d", foo_param); if (foo_param > 10) { do_more_foo(x, y); } }
Los cheros existentes que utilizan banderas de seguimiento privadas pueden cambiarse simplemente aadiendo el siguiente cdigo:
#include "trace.h" /* int my_own_flag = 0; - removed */ #define my_own_flag pg_options[OPT_MY_OWN_FLAG]
388
Todas las pg_options son inicializadas a cero en el arranque del servidor. Si necesitamos un valor de defecto diferente necesitaremos aadir algn cdigo de inicializacin en el principio de PostgresMain. Ahora podemos jar el parmetro foo_param y activar el seguimiento foo escribiendo valores en el chero data/pg_options:
# file pg_options .... foo=1 fooparam=17
Las nuevas opciones sern leidas por todos los nuevos servidores conforme van arrancando. Para hacer efectivos los cambios para todos los servidores que estn en funcionamiento, necesitaremos enviar un SIGHUP al postmaster. La seal ser enviada automticamente a todos los servidores. Podemos activar los cambios tambin para un servidor especco individual enviandole la seal SIGHUP directamente a l. Las pg_options pueden tambin especicarse con el interruptor (switch) -T de Postgres:
postgres options -T "verbose=2,query,hostlookup-"
Las funciones utilizadas para imprimir los errores y los mensajes de debug pueden hacer uso ahora de la facilidad sislog(2). Los mensajes impresos en stdout y stderr son preformatados con una marca horaria que contiene tambin la identicacin del proceso del servidor:
#timestamp 980127.17:52:14.173 980127.17:52:14.174 980127.17:52:14.186 980127.17:52:14.186 #pid [29271] [29271] [29271] [29286] #message StartTransactionCommand ProcessUtility: drop table t; SIIncNumEntries: table is 70% full Async_NotifyHandler
389
Este formato incrementa tambin la capacidad de leer los cheros de mensajes y permite a las personas el conocimiento exacto de lo que cada servidor est haciendo y en qu momento. Tambin hace ms fcil escribir programas con awk o perl que revisen el rastro para detectar errores o problemas en la base de datos, o calcular estadisticas de tiempo de las transacciones. Los mensajes impresos en el syslog utilizan la facilidad de rastro LOG_LOCAL0. El uso de syslog puede ser controlada con la pg_option syslog. Desgraciadamente, muchas funciones llaman directamente a printf() para imprimir sus mensajes en stdout o stderr y su salida no puede ser redirigida a syslog o tener indicaciones cronolgicas en ella. Sera deseable que todas las llamadas a printf fueran reemplazadas con la macro PRINTF y la salida a stderr fuese cambiada para utilizar EPRINTF en su lugar, de modo que podamos controlar todas las salidas de un modo uniforme. El nuevo mecanismo de las pg_options es ms conveniente que denir nuevas opciones de switch en los servidores porque:
No tenemos que denir un switch diferente para cada idea que queramos controlar. Todas las opciones estn denidas como palabras claves en un chero externo almacenado en el directorio de datos. No tenemos que rearrancar Postgres para cambiar el valor de alguna opcin. Normalmente las opciones del servidor se especican al postmaster y pasados a cada servidor cuando sea arrancado. Ahora son leidos de un chero. Podemos cambiar las opciones sobre la marcha mientras el servidor est corriendo. Podemos de este modo investigar algunos problemas activando los mensajes de seguimiento slo cuando aparece el problema. Podemos tambin intentar diferentes valores de parmetros ajustables.
390
# # # #
Notese que keyword puede tambin ser una abreviatura del nombre de opcin denida en backend/utils/misc/trace.c. Reerase al captulo de la Gua del Administrador sobre las opciones de tiempo de ejecucin para una lista completa de opciones soportadas actualmente. Algo del cdigo existente que utiliza variables privadas e interruptores de opciones se han cambiado para hacer uso de las posibilidades de las pg_options, fundamentalmente en postgres.c. Sera deseable modicar todo el codigo existente en este sentido, de modo que podamos hacer uso de muchos de los switches en la lnea de comando de Postgres y poder tener ms opciones ajustables con un lugar nico para situar los valores de las opciones.
391
392
utilizar la DBMS Postgres como software base para sistema de soporte de decisin basado en el conocimiento para mantener un red de energa elctrica. La DBMS necesit manejar consultas con unin para el motor de inferencia del sistema basado en el conocimiento. Las dicultades del rendimiento al explorar el espacio de los posibles planes de la consulta hizo surgir la demanda de un nueva tcnica de optimizacin que se ha desarrollado. A continuacin, proponemos la implementacin de un Algoritmo Gentico como una opcin para el problema de la optimizacin de consultas de la base de datos.
393
P(t) P(t)
+=========================================+ |> Algoritmo AG | +=========================================+ | INICIALIZACIN t := 0 | +=========================================+ | INICIALIZACIN P(t) | +=========================================+ | evaluacin ADAPTABILIDAD de P(t) | +=========================================+ | mientras no CRITERIO DE PARADA hacer | | +-------------------------+ | | P(t) := RECOMBINACIN{P(t)} | | +-------------------------+ | | P(t) := MUTACIN{P(t)} | | +-------------------------+ | | P(t+1) := SELECCIN{P(t) + P(t)} | | +-------------------------+ | | evaluacin ADAPTABILIDAD de P(t) | | +-------------------------+ | | t := t + 1 | +===+=====================================+
394
codicados por cadenas de enteros. Cada cadena representa el orden de la una relacin de unin de la consulta a la siguiente. P. e., el rbol de la consulta
/\ /\ 2 /\ 3 4 1
esta codicado por la cadena de enteros 4-1-3-2, que signica, la primera relacin de unin 4 y 1, despus 3, y despus 2, donde 1, 2, 3, 4 son relids en Postgres. Partes del mdulo OGEC han sido adaptadas del algoritmo Genitor de D. Whitley. Las caractersticas especicas de la implementacin de OGEC en Postgres son:
El uso de un AG en estado constante (remplazo de los individuos con menor adaptacin de la poblacin, no el reemplazo total de un generacin) permite converger rpidamente hacia planes mejorados de consulta. Esto es esencial para el manejo de la consulta en un tiempo razonable; El uso de cruce de recombinacin limitada que esta especialmente adaptado para quedarse con el lmite menor de perdidas para la solucin del PV por medio de un AG; La mutacin como operacin gentica se recomienda a n de que no sean necesarios mecanismos de reparacin para generar viajes legales del PV.
El mdulo OGEC proporciona los siguientes benecios para la DBMS Postgres comparado con la implementacin del optimizador de consultas de Postgres:
El manejo de grandes consultas de tipo unin a travs de una bsqueda no-exhaustiva; Es necesario una mejora en la aproximacin del tamao del coste de los planes de consulta desde la fusin del plan ms corto (el mdulo OGEC evala el coste de un plan de consulta como un individuo).
395
396
Referencias
Informacin de referencia para algoritmos GEQ. The Hitch-Hikers Guide to Evolutionary Computation, Jrg Heitktter y David Beasley, Recurso de InterNet, The Design and Implementation of the Postgres Query Optimizer, Z. Fong, University of California, Berkeley Computer Science Department, Fundamentals of Database Systems, R. Elmasri y S. Navathe, The Benjamin/Cummings Pub., Inc.. FAQ en comp.ai.genetic (news://comp.ai.genetic) esta disponible en Encore (ftp://ftp.Germany.EU.net/pub/research/softcomp/EC/Welcome.html).
397
398
Postgres utiliza un protocolo basado en mensajes para la comunicacin entre frontend y backends. El protocolo est implementado sobre TCP/IP y tambin sobre Unix sockets. Postgres v6.3 introduci nmeros de versin en el protocolo. Esto fue hecho de tal forma que an permite conexiones desde versiones anteriores de los frontends, pero este documento no cubre el protocolo utilizado por esas versiones. Este documento describe la versin 2.0 del protocolo, implementada en Postgres v6.4 y posteriores. Las caractersticas de alto nivel sobre este protocolo (por ejemplo, como libpq pasa ciertas variables de entorno despues de que la comunicacin es establecida), son tratadas en otros lugares.
26.1. Introduccin
Los tres principales componentes son el frontend (ejecutandose en el clicente) y el postmaster y backend (ejecutandose en el servidor). El postmaster y backend juegan diferentes roles pero pueden ser implementados por el mismo ejecutable. Un frontend enva un paquete de inicio al postmaster. Este incluye los nombres del usuario y base de datos a la que el usuario quiere conectarse. El postmaster entonces utiliza esto, y la informacin en el chero pg_hba.conf(5) para determinar que
399
informacin adicional de autenticacin necesita del frontend (si existe) y responde al frontend en concordancia. El frontend enva entonces cualquier informacin de autenticacin requerida. Una vez que el postmaster valida esta informacin responde al frontend que est autenticado y entrega una conexin a un backend. El backend entonces enva un mensaje indicando arranque correcto (caso normal) o fallo (por ejemplo, un nombre de base de datos invlido). Las subsiguientes comunicaciones son paquetes de consulta y resultados intercambiados entre el frontend y backend. El postmaster no interviene ya en la comunicacin ordinaria de cosultas/resultados. Sin embargo el postmaster se involucra cuando el frontend desea cancelar una consulta que se est efectuando en su backend. Ms detalles sobre esto aparecen ms abajo. Cuando el frontend desea desconectar enva un paquete apropiado y cierra la conexin sin esperar una respuesta del backend. Los paquetes son enviados como un ujo de datos. El primer byte determina que se debera esperar en el resto del paquete. La excepcin son los paquetes enviados desde un frontend al postmaster, los cuales incluyen la longitud del paquete y el resto de l. Esta diferencia es histrica.
26.2. Protocolo
Esta seccin describe el ujo de mensajes. Existen cuatro tipos diferentes de ujo dependiendo del estado de la conexin: inicio, consulta, llamada de funcin y nal. Existen tambien provisiones especiales para noticacin de respuestas y cancelacin de comandos, que pueden ocurrir en cualquier instante despues de la fase de inicio.
26.2.1. Inicio
El inicio se divide en fase de autenticacin y fase de arranque del backend.
400
Inicialmente, el frontend enva un StartupPacket. El postmaster utiliza esta informacin y el contenido del chero pg_hba.conf(5) para determinar que mtodo de autenticacin debe emplear. El postmaster responde entonces con uno de los siguientes mensajes: ErrorResponse El postmaster cierra la comunicacin inmediatamente. AuthenticationOk El postmaster entonces cede la comunicacin al backend. El postmaster no toma parte en la comunicacin posteriormente. AuthenticationKerberosV4 El frontend debe tomar parte en una dilogo de autenticacin Kerberos V4 (no descrito aqu) con el postmaster. En caso de xito, el postmaster responde con un AuthenticationOk, en caso contrario responde con un ErrorResponse. AuthenticationKerberosV5 El frontend debe tomar parte en un dilogo de autenticacin Kerberos V5 (no descrito aqu) con el postmaster. En caso de xito, el postmaster responde con un AuthenticationOk, en otro caso responde con un ErrorResponse. AuthenticationUnencryptedPassword El frontend debe enviar un UnencryptedPasswordPacket. Si este es el password correcto, el postmaster responde con un AuthenticationOk, en caso contrario responde con un ErrorResponse. AuthenticationEncryptedPassword El frontend debe enviar un EncryptedPasswordPacket. Si este esel password correcto, el postmaster responde con un AuthenticationOk, en caso contrario responde con un ErrorResponse.
401
Si el frontend no soporta el mtodo de autenticacin requerido por el postmaster, debera cerrar inmediatamente la conexin. Despues de enviar AuthenticationOk, el postmaster intenta lanzar un proceso backend. Como esto podra fallar, o el backend podra encontrar un error durante el arranque, el frontend debe esperar por una conrmacin de inicio correcto del backend. El frontend no debera enviar mensajes en este momento. Los posibles mensajes procedentes del backend durante esta fase son: BackendKeyData Este mensaje es enviado despues de un inicio correcto del backend. Proporciona una clave secreta que el frontend debe guardar is quiere ser capaz de enviar peticiones de cancelacin ms tarde. El frontend no debera responder a este mensaje, pero podra continuar escuchando por un mensaje ReadyForQuery. ReadyForQuery El arranque del backend tuvo xito. El frontend puede ahora enviar mensajes de peticiones o llamandas a funcin. ErrorResponse El arranque del backend no tuvo xito. La conexin es cerrada despues de enviar este mensaje. NoticeResponse Se enva un mensaje de advertencia. El frontend debera mostrar un mensaje pero debera continuar a la espera de un mensaje ReadyForQuery o ErrorResponse.
El mensaje ReadyForQuery es el mismo que el backend debe enviar despues de cada ciclo de consulta. Dependiendo de las necesiades de codicado del frontend, es razonable considerar ReadyForQuery como iniciando un ciclo de consulta (y entonces BackendKeyData indica una conclusin correcta de la fase de inicio), o considerar ReadyForQuery como nalizando la fase de arranque y cada subsiguiente ciclo de consulta.
402
26.2.2. Consulta
Un ciclo de consulta se inicia por el frontend enviando un mensaje Query al backend. El backend entonces enva uno o ms mensajes de respuesta dependiendo del contenido de la cadea de consulta, y nalmente un mensaje ReadyForQuery. ReadyForQuery informa al frontend que puede enviar una nueva consulta o llamada de funcin de forma segura. Los posibles mensajes del backend son: CompletedResponse Una sentencia SQL se complet con normalidad. CopyInResponse El backend est preparado para copiar datos del frontend a una relacin. El frontend debera enviar entonces un mensaje CopyDataRows. El backend responde con un mensaje CompletedResponse con un tag de "COPY". CopyOutResponse El backend est listo para copiar datos de una relacin al frontend. El enva entonces un mensaje CopyDataRows, y un mensaje CompletedResponse con un tag de "COPY". CursorResponse La consulta fue bien un insert(l), delete(l), update(l), fetch(l) o una sentencia select(l). Si la transaccin ha sido abortada entonces el backend enva un mensaje CompletedResponse con un tag "*ABORT STATE*". En otro caso las siguientes respuesta son enviadas. Para una sentencia insert(l), el backend enva un mensaje CompletedResponse con un tag de "INSERT oid rows" donde rows es el nmero de las insertadas, y oid es el ID de objeto de la la insertada si rows es 1, en otro caso oid es 0. Para una sentencia delete(l), el backend enva un mensaje CompletedResponse con un tag de "DELETE rows" donde rows es el nmero de las borradas.
403
Para una sentencia update(l) el backend enva un mensaje CompletedResponse con un tag de "UPDATE rows" donde rows es el nmero de las modicadas. para una sentencia fetch(l) o select(l), el backend enva un mensaje RowDescription. Es seguido despus con un mensaje AsciiRow o BinaryRow (dependiendo de si fu especicado un cursor binario) para cada la que es envada al frontend. Por ltimo, el backend enva un mensaje CompletedResponse con un tag de "SELECT".
EmptyQueryResponse Se encontro una caden de consulta vaca. (La necesidad de distinguir este caso concreto es histrica). ErrorResponse Ocurri un error. ReadyForQuery El procesado de la cadena de consulta se complet. Un mensaje seperado es enviado para indicar esto debido a que la cadena de consulta puede contener mltiples sentencias SQL. (CompletedResponse marca el nal el procesado del una sentencia SQL, no de toda la cadena). Siempre se enviar ReadyForQuery, bien el procesado terminase con xito o con error. NoticeResponse Un mensaje de advertencia fu enviado en relacin con la consulta. Estas advertencias se envan en adicin a otras respuestas, es decir, el backend continuar procesando la sentencia.
Un frontend debe estar preparado para aceptar mensaje ErrorResponse y NoticeResponse cuando se espere cualquier otro tipo de mensaje.
404
De hecho, es posible que NoticeResponse se reciba incluso cuando el frontned no est esperando ningn tipo de mensaje, es decir, cuando el backend est normalmente inactivo. En particular, el frontend puede solicitar la nalizacin del backend. En este caso se enva una NoticeResponse antes de cerrar la conexin. Se recomienda que el frontend compruebe esas advertencias asncronas antes de enviar cada sentencia. Tambin, si el frontend enva cualquier comando listen(l), entonces debe estar preparado para aceptar mensajes NoticationResponse en cualquier momento. Vase ms abajo.
405
NoticeResponse Un mensaje de advertencia se gener en relacin con la llamada a funcin. Estas advertencias aparecen en adicin a otras respuestas, es decir, el backend continuar procesando el comando.
El frontend debe estar preparado para aceptar mensajes ErrorResponse y NoticeResponse cuando se esperen otro tipo de mensajes. Tambin si enva cualquier comando listen(l) debe estar preparado para aceptar mensajes NoticationResponse en cualquier momento, vase ms abajo.
Puede merecer la pena apuntar que los nombres utilizados en los comandos listen y notify no necesitan tener nada que ver con los nombres de relaciones (tablas) y bases de datos SQL. Los nombres de noticacin son simplemente nombres arbitrariamente seleccionados.
406
407
riesgo de seguridad, ya que personas no autorizadas podran intentar cancelar consultas. El riesgo de seguridad es afrontado requiriendo la clave secreta generada dinmicamente.
26.2.6. Finalizacin
El procedimiento de nalizacin normal es que el frontend enve un mensaje Terminate y cierre inmediatamente la conexin. Al recibir el mensaje, el backend cierra inmediatamente la conexin y naliza. Una nalizacin anormal puede ocurrir debido a fallos de software (i.e. core dump) en cualquier extremo. Si el frontend o el backend ve un cierre inexperado de la conexin, debera liberar resursos y nalizar. El frontend tiene la opcin de lanzar un nuevo backen recontactando el postmaster, si lo desea.
408
String(s) Una cadena de C convencional terminada en \0 sin limitacin de longitud. Si s est especicada es el valor literal. P.e. String, String("user").
Nota: No existe lmite predenido para la longitud de una cadena que puede ser retornada por el backend. Una buena estrategia a utilizar por el frontend consiste en usar un buffer expandible para que cualquier cosa que quepa en memoria pueda ser aceptada. Si esto no es posible, se debe leer toda la cadena y deshechar los caracteres que no quepan en el buffer de longitud ja.
Byten(c) Exactamente n bytes. Si c est especicade es el valor literal. P.e. Byte, Byte1(\n).
Byte1(D) Identica el mensaje como una la de datos ASCII . (Un mensaje previo RowDescription dene el nmero de campos en la la y sus tipos de datos).
409
Byten Un mapa de bits con un bit para cada campo en la la. El primer campo corresponde al bit 7 (MSB) del primer byte, el segundo campo corresponde al bit 6 del primer byte, el octavo campo corresponde al bit 0 (LSB) del primer byte, el noveno campo corresponde al bit 7 del segundo byte, y as sucesivamente. Cada bit est activo si el valor del campo correspondiente no es NULL. Si el nmero de campos no es un mltiplo de 8, el resto del ltimo byte en el mapa de bits no es utilizado. Por lo tanto, para cada campo con un valor no NULL, tenemos lo siguiente: Int32 Especica el tamao del valor del campo, incluyendo este tamao. Byten Especica el valor del campo mismo en caracteres ASCII. n es el anterior tamao menos 4. No hay \0 al nal del campo de datos, el frontend debe aadirlo si quiere uno.
AuthenticationOk (B)
Byte1(R) Identica el mensaje como una peticin de autenticacin. Int32(0) Especica que la autenticacin tuvo xito.
410
AuthenticationKerberosV4 (B)
Byte1(R) Identica el mensaje como una peticin de autenticacin. Int32(1) Especica que se requiere autenticacin Kerberos V4. AuthenticationKerberosV5 (B)
Byte1(R) Identica el mensaje como una peticin de autenticacin. Int32(2) Especica que se requiere autenticacin Kerberos V5. AuthenticationUnencryptedPassword (B)
Byte1(R) Identica el mensaje como una peticin de autenticacin. Int32(3) Especica que se requiere una contrasea no encriptada.
411
AuthenticationEncryptedPassword (B)
Byte1(R) Identica el mensaje como una peticin de autenticacin. Int32(4) Especica que se requiere una contrasea encriptada. Byte2 El salto a utilizar al encriptar la contrasea. BackendKeyData (B)
Byte1(K) Identica el mensaje como una clave de cancelacin. El frontend debe guardar estos valore se desea poder enviar mensajes CancelRequest posteriormente. Int32 El ID de proceso del backend. Int32 La clave secreta de este backend.
412
BinaryRow (B)
Byte1(B) Identica el mensaje como una la de datos binarios. (Un mensaje RowDescription previo dene el nmero de campos en la al y sus tipos de datos) Byten Un mapa de bits con un bit para cada campo en la la. El primer campo corresponde al bit 7 (MSB) del primer byte, el segundo campo corresponde al bit 6 del primer byte, el octavo campo corresponde al bit 0 (LSB) del primer byte, el noveno campo corresponde al bit 7 del segundo byte, y as sucesivamente. Cada bit est activo si el valor del campo correspondiente no es NULL. Si el nmero de campos no es un mltiplo de 8, el resto del ltimo byte en el mapa de bits no es utilizado. Para cada campo con un valor distinto de NULL, tenemos lo siguiente: Int32
Especica el tamao del valor del campo, excluyendo este tamao. *********************************************************************** *************************** Comprobar esto, por que aqu dice _excluyendo_ y antes (lnea 756) dice incluyendo??????????????***************** *********************************************************************** Byten Especica el valor del campo mismo en formato binario. n es el tamao previo.
413
CancelRequest (F)
Int32(16) El tamao del paquete en bytes. Int32(80877102) El cdigo de cancelacin de peticin. El valor es elegido para que contenga "1234" el los 16 bits ms signicativos, y "5678" en los 16 bits menos signicativos. Para evitar consin, este cdigo no debe ser el mismo que ningn nmero de versin del protocolo. Int32 El ID de proceso del backend objetivo. Int32 La clave secreta para el backend objectivo. CompletedResponse (B)
Byte1(C) Identica este mensaje como una peticin completada. String El comando. Normalmente (pero no siempre) una palabra simple que identica que comando SQL se complet. CopyDataRows (B y F) Es un ujo de las donde cada una est terminada por un Byte1(\n). Se completa con una secuencia Byte1(\\), Byte1(.), Byte1(\n).
414
CopyInResponse (B)
Byte1(G) Identica el mensaje como una respuesta Start Copy In. El frontend debe enviar un mensaje CopyDataRows. CopyOutResponse (B)
Byte1(H) Identica el mensaje como una respuesta Start Copy Out. Este mensaje ser seguido por un mensaje CopyDataRows. CursorResponse (B)
Byte1(P) Identica el mensaje como un cursor. String El nombre del cursor. Ser "blanco" si el cursor es implcito. EmptyQueryResponse (B)
Byte1(I) Identica este mensaje como una respuesta a una sentencia vaca.
415
Int32 El tamao del paquete en bytes. String La contrasea encriptada (mediante crypt()). ErrorResponse (B)
Byte1(E) Identica el mensaje como un error. String El mensaje de error mismo. FunctionCall (F)
Byte1(F) Identica el mensaje como una llamada a funcin. String("") Sin utilizar.
416
Int32 Especica el ID de objeto de la funcin a llamar. Int32 Especica el nmero de argumentos que se suministran a la funcin. Para cada argumento, se tiene lo siguiente: Int32 Especica el tamao del valor del argumento, excluyendo este tamao. Byten Especica el valor del campo mismo en formato binario. n es el tamao anterior.
FunctionResultResponse (B)
Byte1(V) Identica el mensaje como un resultado de llamada a funcin. Byte1(G) Especica que se devolvi un resultado no vaco. Int32 Especica el tamao del valor del resultado, excluyendo este tamao. Byten Especia el valor del resultado en formato binario. n Es el tamao anterior.
417
Byte1(0) Sin utilizar. (Hablando propiamente, FunctionResultResponse y FunctionVoidResponse son lo mismo pero con algunas partes opcionales en el mensaje). FunctionVoidResponse (B)
Byte1(V) Identica el mensaje como un resultado de llamada a funcin. Byte1(0) Especica que se devolvi un resultado vaco. NoticeResponse (B)
Byte1(N) Identica el mensaje como una advertencia. String El mensaje de advertencia mismo. NoticationResponse (B)
418
Int32 El ID de proceso del proceso backend. String El nombre de la condicin en la que se lanz la noticacin. Query (F)
Byte1(Q) Identica el mensaje como una peticin. String La peticin misma. ReadyForQuery (B)
Byte1(Z) Identica el tipo de mensaje. ReadyForQuery es enviado cuando el backend est listo para un nuevo ciclo de peticin. RowDescription (B)
Byte1(T) Identica el mensaje como una descripcin de la. Int16 Especica el nmero de campos en una la (puede ser cero).
419
Para cada campo tenemos lo siguiente: String Especica el nombre del campo. Int32 Especica el ID de objeto del tipo de campo. Int16 Especica el tamao del tipo. Int32 Especica el modicador del tipo.
StartupPacket (F)
Int32(296) El tamao del paquete en bytes. Int32 El nmero de versin del protocolo. Los 16 bits ms signicativos son el numero de versin mayor. Los 16 bits menos signicativos son el nmero de versin menor. LimString64 El nombre de la base de datos, por defecto el nombre del usuario si no se especica.
420
LimString32 El nombre del usuario. LimString64 Cualquier linea de argumentos para pasar al backend por el postmaster. LimString64 Sin utilizar. LimString64 La tty opcional que el backen debera utilizar para mensajes de depuracin. Terminate (F)
421
Postgres usa las siguientes seales para la comunicacin entre el postmaster y los backends: Tabla 27-1. Seales Postgres Signal SIGHUP SIGINT SIGQUIT SIGTERM SIGPIPE SIGUSR1 SIGUSR2 SIGCHLD SIGTTIN SIGTTOU SIGCONT SIGFPE Accin postmaster kill(*,sighup) die kill(*,sigterm) kill(*,sigterm), kill(*,9), die ignored kill(*,sigusr1), die kill(*,sigusr2) reaper ignorado ignorado dumpstatus
Accin del se read_pg_option cancela la cons handle_warn muerte muerte muerte rpida noticacin as ignorado (prue
FloatException
422
Los principales cambios del viejo gestor de seal es el uso de SIGQUIT en lugar de SIGHUP para gestionar los avisos, SIGHUP intenta releer el chero de pg_options y lo redirecciona a todos los backends activos de SIGHUP, SIGTERM, SIGUSR1 y SIGUSR2 llamados por el postmaster. Por este camino estas seales enviada al postmaster pueden ser enviadas automticamente hacia todos los backends sin necesidad de conocer sus pids. Para bajar postgres lo nico que se necesita es enviar un SIGTERM al postmaster y esto parar automticamente todos los backends. La seal SIGUSR2 es tambin usado para prevenir el desbordamiento del cache de la tabla SI esto pasa cuando algn backend no procesa la cache SI durante un largo periodo de tiempo. Cuando el backend detecta que la tabla SI esta a mas de un 70% simplemente enva una seal al postmaster el cual despertar a todos los backends desocupados y los hace que vace el cache. El uso tpico de las seales por los programadores puede ser el siguiente:
# stop postgres kill -TERM $postmaster_pid # kill all the backends kill -QUIT $postmaster_pid # kill only the postmaster kill -INT $postmaster_pid # change pg_options cat new_pg_options > $DATA_DIR/pg_options kill -HUP $postmaster_pid # change pg_options only for a backend cat new_pg_options > $DATA_DIR/pg_options kill -HUP $backend_pid cat old_pg_options > $DATA_DIR/pg_options
423
Para congurar gcc para usar ciertas opciones por defecto, simplemente hay que editar el chero /usr/local/lib/gcc-lib/platform/version/specs. El formato de este chero es bastante simple. El chero est dividido en secciones, cada una de tres lineas de longitud. La primera es "*section_name:" (e.g. "*asm:"). La segunda es una linea de opciones, y la tercera es una linea en blanco. El cambio ms sencillo es aadir las opciones deseadas a la lista en la seccin apropiada. Por ejemplo, supongamos que tenemos Linux ejecutandose enun 486 con gcc 2.7.2 instalado en su lugar por defecto. En el chero /usr/local/lib/gcc-lib/i486-linux/2.7.2/specs, 13 lineas ms abajo se encuentra la siguiente seccin:
- -------SECTION------*cc1:
- -------SECTION-------
Como puede verse, no hay ninguna opcin por defecto. Si siempre compila codigo C usando "-m486 -fomit-frame-pointer", tendria que cambiarlo de este modo:
- -------SECTION------*cc1: - -m486 -fomit-frame-pointer - -------SECTION-------
424
Si queiero poder generar codigo 386 para otro equipo Linux ms antiguo que tenga por ah, tendramos que hacer algo as:
- -------SECTION------*cc1: %{!m386:-m486} -fomit-frame-pointer - -------SECTION-------
Esto omite siempre los punteros de marco; se construir codigo optimizado para 486 a menos que se especique -m386 en la linea de ordenes. Pueden realizarse bastantes personalizaciones usando el chero spect. Sin embargo, reuerde siempre que esos cambios son globales, y afectarn a todos los usuarios del sistema.
425
426
y se redene usando la misma sintaxis que en la denicin. Seguidamente se listan los comandos generales y los comandos de macro.
427
CREATE classname (name1 = type1 [,name2 = type2[,...]]) Crea una clase llamada classname con los atributos introducidos entre parntesis. OPEN (name1 = type1 [,name2 = type2[,...]]) AS classname Abre una clase llamada classname para escritura pero no graba su existencia en los catlogos de sistema. (Esto es primordialmente lo que ayuda al bootstrapping.) DESTROY classname Destruye la clase llamada classname. DEFINE INDEX indexname ON class_name USING amname (opclass attr | (function(attr )) Crea un ndice llamado indexname para la clase llamada classname usando el metodo de acceso amname. Los campos se llaman name1, name2 etc., y los operadores de recogida que usa son collection_1, collection_2 etc., respectivamente.
Nota: Esta ltima sentencia no referencia a nada del ejemplo. Deberia ser cambiado para que tenga sentido. - Thomas 1998-08-04
428
DEFINE MACRO macro_name FROM FILE filename Dene una macro llamada macro_name la cual tendr un valor que se leer del archivo filename.
r Visualiza aleatoriamente la clase abierta. m -1 Cambia la visualizacin de la informacin del tiempo. m0 Activa la recuperacin inmediatamente. m 1 Jan 1 01:00:00 1988 Activa la recuperacin de la foto para un tiempo especco. m 2 Jan 1 01:00:00 1988, Feb 1 01:00:00 1988 Activa la recuperacin para un rango especico de tiempo. Ambos tiempos deben ser reemplazados por un espacio en blanco si el rango de tiempo deseado es ilimitado.
429
&A classname natts name1 type1 name2 type2 ... Aade atributos chivato llamados name1, name2, etc. de tipos type1, type2, etc. para la clase classname. &RR oldclassname newclassname Renombra la clase oldclassname por newclassname. &RA classname oldattname newattname classname oldattname newattname Renombra el atributo oldattname de la clase llamada classname por el newattname.
29.5. Ejemplo
El siguiente conjunto de comandos crear la clase pg_opclass conteniendo una coleccin de int_ops como un objeto con un OID igual a 421, visualiza la clase , y despus la cierra.
create pg_opclass (opcname=name) open pg_opclass insert oid=421 (int_ops) print close pg_opclass
430
Esta seccin proporciona una visin general del formato de pgina utilizado por las clases de Postgres. Los mtodos de acceso denidos por el usuario no necesitan utilizar este formato de pgina. En la siguiente explicacin, se asume que un byte contiene 8 bits. Adems, el trmino campo se reere a los datos almacenados en una clase de Postgres.
431
Descripcin
Los primeros 8 bytes de cada pgina consiten en la cabecera de la pgina (PpageHeaderData). Dentro de la cabecera, los primeros tres campos enteros de 2 bytes (menor, mayor y especial) representan bytes que reejan el principio del espacio desocupado, el nal del espacio desocupado, y el principio del espacio especial. El espacio especial es una regin al nal de la pgina que se ocupa en la inicializacin de la pgina y que contiene informacin especca sobre un mtodo de acceso. Los dos ltimos 2 bytes de la cabecera de pgina, opaco, codica el tamao de la pgina e informacin sobre la fragmentacin interna de la misma. El tamao de la pgina se almacena en cada una de ellas, porque las estructuras del pool de buffers pueden estar subdivididas en una forma estructura por estructura dentro de una clase. La informacin sobre la fragmentacin interna se utiliza para ayudar a determinar cuando debera realizarse la reorganizacin de la pgina. Siguiendo a la cabecera de la pgina estn los identicadores de campo (ItemIdData). Se situan nuevos identicadores de campo a partir de los primeros cuatro bytes de espacio libre. Debido a que un identicador de campo nonca se mueve hasta que se elimina, este ndice se puede utilizar para indicar la situacin de un campo en l apgina. De hecho, cada puntero a un campo (ItemPointer) creado por Postgres consiste en un nmero de estructura y un ndice de un identicador de campo. Un identicador de campo contiene un byte de referencia al principio de un campo, su longitud en bytes, y un conjunto de bits de atributos que pueden afectar a su interpretacin. Los campos mismos estn almacenados en un espacio situado ms all del nal del espacio libre. Habitualmente, los campos no son interpretados. Sin embargo, cuando el campo es demasiado largo para ser situado en una nica pgina o cuando se desea la fragmentacin del campo, ste mismo se divide y cada parte se manipula como campos distintos de la siguiente manera. Cada una de las partes en que se descompone se situa en una estructura de continuacin de campo (ItemContinuationData). Esta estructura contiene un puntero (ItemPointerData) hacia la siguiente parte. La ltima de estas partes se manipula normalmente.
432
30.2. Ficheros
data/
30.3. Bugs
El formato de la pgina puede cambiar en el futuro para proporcionar un acceso ms eciente a los objetos largos. Esta seccin contiene detalles insuciente para ser de alguna asistencia en la escritura de un nuevo mtodo de acceso.
433
(N. del T: Ismael Olea ha escrito un estupendo documento llamado Micro-cmo empezar a trabajar con cvs, muy facil de entender y de utilizar, y que puede resultar muy interesante para los que slo deseen utilizar un cliente de CVS de modo genrico. Como l tambin colabora en la traduccin, no puedo por menos de recomendarlo. Lo pueden conseguir en su pgina personal (http://slug.HispaLinux.ES/~olea/micro-como-empezar-con-cvs.html) y desde luego pidiendoselo directamente a l olea@hispafuentes.com (mailto:olea@hispafuentes.com). Fin de la N. del T.) El comando cvs checkout tiene un indicador (ag), -r, que le permite comprobar una cierta revisin de un mdulo. Este indicador facilita tambin, por ejemplo, recuperar las fuentes que formaban la release 1.0 del mdulo tc en cualquier momento futuro:
434
Esto es utilizable, por ejemplo, si alguien asegura que hay un error (un bug) en esa release, y usted no es capaz de encontrarlo en la copia de trabajo actual.
Sugerencia: Tambin puede usted comprobar un mdulo conforme era en cualquier momento dado utilizando la opcin -D.
Cuando etiquete usted ms de un chero con la misma etiqueta, puede usted pensar en las etiquetas como "una lnea curva que recorre una matriz de nombres de cheros contra nmero de revisin". Digamos que tenemos 5 cheros con las siguientes revisiones:
fich1 fich2 fich3 fich4 fich5 <-*TAG (etiqueta)
1.1 1.1 1.1 1.1 /-1.1* 1.2*1.2 1.2 -1.2*1.3 \- 1.3*1.3 / 1.3 1.4 \ 1.4 / 1.4 \-1.5*1.5 1.6
435
lo cual crear la etiqueta y la rama para el rbol RELEASE. Ahora, para aquellos con acceso CVS, tambin es sencillo. Primero, cree dos subdirectorios, RELEASE y CURRENT, de forma que no mezcle usted los dos. A continuacin haga:
cd RELEASE cvs checkout -P -r REL6_4 pgsql cd ../CURRENT cvs checkout -P pgsql
lo que dar lugar a dos rboles de directorios, RELEASE/pgsql y CURRENT/pgsql. A partir de este momento, CVS tomar el control de qu rama del repositorio se encuentra en cada rbol de directorios, y permitir actualizaciones independientes de cada rbol. Si usted slo est trabajando en el rbol fuente CURRENT hgalo todo tal como empezamos antes etiquetando las ramas de la release. If you are only working on the CURRENT source tree, you just do everything as before we started tagging release branches. Una vez que usted realiza el checkout (igualado, comprobacin, descarga) inicial en una rama,
$ cvs checkout -r REL6_4
todo lo que usted haga dentro de esa estructura de directorios se restringe a esa rama. Si usted aplica un patch a esa estructura de directorios y hace un
cvs commit
436
mientras usted se encuentra dentro de ella, el patch se aplica a esa rama y slo a esa rama.
2.
Se le preguntar us password; introduzca postgresql. Slo necesitar hacer esto una vez, pues el password se almacenar en .cvspass, en su directorio de defecto (your home directory). 3. Descargue las fuentes de Postgres:
cvs -z3 -d :pserver:anoncvs@postgresql.org:/usr/local/cvsroot co P pgsql
lo cual instala las fuentes de Postgres en un subdirectorio pgsql del directorio en el que usted se encuentra.
437
Nota: Si tiene usted una conexin rpida con Internet, puede que no necesite -z3, que instruye a CVS para utilizar compresin gzip para la transferencia de datos. Pero en una conexin a velocidad de modem, proporciona una ventaja muy sustancial.
Esta descarga inicial es un poco ms lenta que simplemente descargar un chero tar.gz; con un modem de 28.8K, puede tomarse alrededor de 40 minutos. La ventaja de CVS no se muestra hasta que intenta usted actualizar nuevamente el chero. 4. Siempre que quiera usted actualizar las ltimas fuentes del CVS, cd al subdirectorio pgsql, y ejecute
$ cvs -z3 update -d -P
Esto descargar slo los cambios producidos desde la ltima actualizacin realizda. Puede usted actualizar en apenas unos minutos, tpicamente, incluso con una lnea de velocidad de modem. 5. Puede usted mismo ahorrarse algo de tecleo, creando un chero .cvsrc en su directorio de defecto que contenga:
cvs -z3 update -d -P
Esto suministra la opcin -z3 a todos los comandos al cvs, y las opciones -d y -P al comando cvs update. Ahora, simplemente tiene que teclear
$ cvs update
438
Atencin
Algunas versiones anteriores de CVS tenan un error que llevaba a que todos los cheros comprobados se almacenasen con permisos de escritura para todo el mundo (777) en su directorio. Si le ha pasado esto, puede usted hacer algo como
$ chmod -R go-w pgsql
para colocar los permisos adecuadamente. Este error se j a partir de la versin 1.9.28 de CVS.
CVS puede hacer un montn de otras cosas, del tipo de recuperar revisiones previas de los fuentes de Postgres en lugar de la ltima versin de desarrollo. Para ms informacin, consulte el manual que viene con CVS, o mire la documentacin en lnea en http://www.cyclic.com/.
439
en su chero .cshrc, o una lnea similar en su chero .bashrc o .profile, dependiendo de su shell. Se debe inicializar el rea del repositorio de cvs. Una vez que se ja CVSROOT, se puede hacer esto con un nico comando:
$ cvs init
tras lo cual, debera usted ver al menos un directorio llamado CVSROOT cuando liste el directorio CVSROOT:
$ ls $CVSROOT CVSROOT/
440
donde -L 2 activa algunos mensajes de status para que pueda usted monitorizar el progreso de la actualizacin, y postgres.cvsup es la ruta y el nombre que usted ha dado a su chero de conguracin de CVSup. Aqu le mostramos un cheros de conguracin de CVSup modicado para una instalacin especca, y que mantiene un repositorio CVS local completo: (N. del T: voy a traducir los comentarios a modo de documentacin del chero. Obviamente, no traducir los comandos, lo que puede dar una imagen algo complicada, pero me parece que puede valer la pena. Agradeceremos sus comentarios a doc-postgresql-es@hispalinux.es)
# Este fichero representa el fichero de distribucin estandar de CVSup # para el proyecto de ORDBMS PostgreSQL. # Modificado por lockhart@alumni.caltech.edu 1997-08-28 # - Apunta a mi foto fija local del rbol fuente. # - Recupera el repositorio CVS completo, y no slo la ltima actualizacin. # # Valores de defecto que se aplican a todas las recolecciones. *default host=postgresql.org *default compress *default release=cvs *default delete use-rel-suffix # activar la lnea siguiente para tomar la ltima actualizacin. #*default tag=.
441
# activar la lnea siguiente para tomar todo lo que se ha especificado antes # o por defecto en la fecha especificada a continuacin. #*default date=97.08.29.00.00.00
# el directorio base apunta a donde CVSup almacenar sus ficheros de marcas. # crear un subdirectorio sup/ #*default base=/opt/postgres # /usr/local/pgsql *default base=/home/cvs # el directorio prefijo apunta a donde CVSup almacenar la/s distribucin/es actuales. *default prefix=/home/cvs # la distribucin completa, incluyendo todo lo siguiente. pgsql # # # # distribuciones individuales contra el paquete completo pgsql-doc pgsql-perl5 pgsql-src
El siguiente chero de conguracin de CVSup se sugiere en el servidor ftp de Postgres (ftp://ftp.postgresql.org/pub/CVSup/README.cvsup) y descargar nicamente la foto ja actual:
# Este fichero representa el fichero de distribucin estandar de CVSup # para el proyecto de ORDBMS PostgreSQL. # # Valores de defecto que se aplican a todas las recolecciones, a todas las descargas. *default host=postgresql.org *default compress
442
# el directorio base apunta a donde CVSup almacenar sus ficheros de marcas. *default base=/usr/local/pgsql # el directorio prefijo apunta a dnde CVSup almacenar las distribuciones actuales. *default prefix=/usr/local/pgsql # distribucin completa, incluyendo todo lo siguiente. pgsql # # # # distribuciones individuales contra el paquete completo pgsql-doc pgsql-perl5 pgsql-src
443
utilizando FreeBSD, para el que CVSup est disponible como una adaptacin (porting).
Nota: CVSup fue desarrollado originariamente como una herramienta para la distribucin del rbol fuente de FreeBSD. Est disponible como una adaptacin, y para aquellos que utilizan FreeBSD, si esto no es suciente para decirles como obtenerlo e instalarlo, les agradeceremos que nos aporten un procedimiento ecaz.
En el momento de escribir, se disponen binarios para Alpha/Tru64, ix86/xBSD, HPPA/HPUX-10.20, MIPS/irix, ix86/linux-libc5, ix86/linux-glibc, Sparc/Solaris, and Sparc/SunOS. 1. Adquiera el chero tar con los binarios para cvsup (cvsupd no se requiere para ser un cliente) adecuado para su plataforma. a. b. 2. Si utiliza usted FreeBSD, instale la adaptacin de CVSup. Si tiene usted otra plataforma, localice y descargue los binarios apropiados desde el servidor ftp de Postgres (ftp://postgresql.org/pub).
Compruebe el chero tar para vericar el contenido y la estructura de directorios, si la hay. Al menos para el chero tar de linux, los binarios estticos y las pginas man se incluyen sin ningn empaquetado de directorios. a. Si el binario se encuentra en el nivel superior del chero tar, simplemente desempaquete el chero tar en su directorio elegido:
$ cd /usr/local/bin $ tar zxvf /usr/local/src/cvsup-16.0-linux-i386.tar.gz $ mv cvsup.1 ../doc/man/man1/
444
b.
Si hay una estructura de directorios en el chero tar, desempaquete el chero tar en /usr/local/src, y mueva los binarios a la direccin adecuada como antes.
3.
Instalacin en Linux 1. Instale Modula-3. a. Tome la distribucin de Modula-3 desde Polytechnique Montral (http://m3.polymtl.ca/m3), quien mantiene activamente el cdigo base
445
originalmente desarrollado por the DEC Systems Research Center (http://www.research.digital.com/SRC/modula-3/html/home.html). La distribucin RPM PM3 est comprimida aproximadamente unos 30 MB. En el momento de escribir, la versin 1.1.10-1 se instalaba lmpiamente en RH-5.2, mientras que la 1.1.11-1 estaba construda aparentemente para otra versin (RH-6.0?) y no corra en RH-5.2.
Sugerencia: Este empaquetado rpm particular tiene muchos cheros RPM, de modo que seguramente quiera usted situarlos en un directorio aparte.
b.
2.
3.
Construya la distribucin de cvsup, suprimiento la interface grca para evitar la necesidad de las libreras X11:
# make M3FLAGS="-DNOGUI"
Y si quiere construir un binario esttico para trasladarlo a sistemas en los cuales no pueda tener instalado Modula-3, intente:
# make M3FLAGS="-DNOGUI -DSTATIC"
446
4.
447
Texto plano con informacin acerca de la pre-instalacin. HTML, que se usa para navegacin on-line y como referencia. Documentacin impresa para lecturas ms detenidas y tambin como referencia. pginas man como referencia rpida.
Tabla DG2-1. Documentacin de Postgres Fichero ./COPYRIGHT ./INSTALL ./README ./register.txt ./doc/bug.template ./doc/postgres.tar.gz Descripcin Apuntes de Copyright Instrucciones de instalacin (textos sgml->rtf->text) Informacin introductoria Mensajes de registro durante make Plantilla para informes de depuracin Documentos integrados (HTML)
448
Descripcin Gua del programador (Postscript) Gua del programador (HTML) Manual de referencia (Postscript) Manual de referencia (HTML) Introduccin (Postscript) Introduccin (HTML) Gua del usuario (Postscript) Gua del usuario (HTML)
Se disponen de pginas man, as como como un gran nmero de cheros de texto del tipo README en todas las fuentes de Postgres .
449
(http://www.ora.com/homepages/dtdparse/docbook/3.0/) le da una poderosa referencia de las caractersticas de DocBook. El conjunto de esta documentacin se ha construido usando varias herramientas, incluyendo jade (http://www.jclark.com/jade/) de James Clark y Modular DocBook Stylesheets (http://www.nwalsh.com/docbook/dsssl/) de Norm Walsh. Normalmente las copias impresas se producen importando cheros Rich Text Format (RTF) desde jade a ApplixWare para eliminar algn error de formato y a partir de aqu exportarlo como chero Postscript. TeX (http://sunsite.unc.edu/pub/packages/TeX/systems/unix/) es un formato soportado como salida por jade, pero no se usa en estos momentos, principalmente por su incapacidad para hacer formateos pequeos del documento antes de la copia impresa y por el inadecuado soporte de tablas en las hojas de estilo TeX.
450
451
DG2.3.3.1. emacs/psgml
emacs (y xemacs) tienen un modo de trabajo (major mode) SGML. Si se congura correctamente le permitir utilizar emacs para insertar etiquetas y controlar la consistencia de las marcas. Introduzca las siguientes lneas en su chero ~/.emacs (ajustando el path si fuera necesario):
; ********** for SGML mode (psgml) (setq sgml-catalog-files "/usr/lib/sgml/CATALOG")
452
(setq sgml-local-catalogs "/usr/lib/sgml/CATALOG") (autoload sgml-mode "psgml" "Major mode to edit SGML files." t )
y aada una entrada en el mismo chero para SGML en la denicin existente para auto-mode-alist:
(setq auto-mode-alist (("\\.sgml$" . sgml-mode) ))
Cada chero fuente SGML tiene el siguiente bloque al nal del chero:
!- Mantenga este comentario al final del fichero Local variables: mode: sgml sgml-omittag:t sgml-shorttag:t sgml-minimize-attributes:nil sgml-always-quote-attributes:t sgml-indent-step:1 sgml-indent-data:t sgml-parent-document:nil sgml-default-dtd-file:"./reference.ced" sgml-exposed-tags:nil sgml-local-catalogs:("/usr/lib/sgml/catalog") sgml-local-ecat-files:nil End: -
453
Cuando est usando emacs/psgml, una manera cmoda de trabajar con los cheros separados de partes del libro es insertar una declaracin DOCTYPE mientras los est editando. Si, por ejemplo, usted estuviera trabajando sobre este chero fuente, que es un apndice, usted especicara que es una instancia "appendix" de un documento DocBook haciendo que la primera lnea sea parecida a esto:
!doctype appendix PUBLIC "-//Davenport//DTD DocBook V3.0//EN"
Esa lnea signica que cualquier cosa que sea capaz de leer SGML lo tomar correctamente y se puede vericar el documento con "nsgmls -s docguide.sgml".
454
donde HSTYLE y PSTYLE determinan el path a docbook.dsl para hojas de estilo HTML y copias impresas respectivamente. Estos cheros de hojas de estilo son para el Modular Style Sheets, de Norm Walsh. Si se usan otras hojas de estilo, entonces se pueden denir HDSL y PDSL como el path completo y como nombre del chero de la hoja de estilo como se hizo ms arriba con HSTYLE y PSTYLE. En muchos sistemas estas hojas de estilo se encontrarn en en paquetes instalados en /usr/lib/sgml/, /usr/share/lib/sgml/, o /usr/local/lib/sgml/. Los paquetes de documentacin HTML pueden ser generados desde la fuente SGML escribiendo lo siguiente:
% % % % % % % cd doc/src make tutorial.tar.gz make user.tar.gz make admin.tar.gz make programmer.tar.gz make postgres.tar.gz make install
Estos paquetes pueden ser instalados desde el directorio principal de la documentacin escribiendo:
% cd doc % make install
455
utilidad requera un parche para ejecutar correctamente las marcas de Postgres y hemos aadido una pequea cantidad de nuevas funciones que permiten congurar la seccin de pginas man en el chero de salida. docbook2man est escrita en perl y requiere el paquete CPAN SGMLSpm para funcionar. Tambin requiere nsgmls, que se incluye en la distribucin de jade . Despus de instalar estos paquetes, simplemente ejecute:
$ cd doc/src $ make man
que generar un chero tar en el directorio doc/src. Proceso de instalacin de docbook2man 1. 2. 3. Instale el paquete docbook2man, que se encuentra disponible en http://shell.ipoline.com/~elmert/comp/docbook2X/ Instale el mdulo perl SGMLSpm, disponible en las diferentes rplicas de CPAN. Instale nsgmls, si todava no lo tiene de instalacin de jade.
456
cheros estn en un formato de texto plano, pero provienen de cheros fuente SGML. Generacin de texto plano Tanto INSTALL como HISTORY se generan desde fuentes SGML que ya existen. Se extraen desde el chero RTF intermedio. 1. Para generar RTF escriba:
% cd doc/src/sgml % make installation.rtf
2. 3.
Importe installation.rtf a Applix Words. Fije el ancho y los mrgenes de la pgina. a. b. Ajuste el ancho de pgina en File.PageSetup a 10 pulgadas. Seleccione todo el texto. Ajuste el margen derecho a 9,5 pulgadas utilizando la regla. Esto dar un ancho de columna mximo de 79 caracteres, que esta dentro del lmite superior de las 80.
4.
Elimine las partes del documento que no sean necesarias. Para INSTALL, elimine todas las notas de la versin desde el nal del texto a excepcin de aquellas que tengan que ver con la versin actual. Para HISTORY, elimine todo el texto que est por encima de las notas de versin, preservando y modicando el ttulo y el ToC.
5. 6.
Exporte el resultado como ASCII Layout. Usando emacs o vi, elimine la informacin de tablas en INSTALL. Borre los mailto URLs de los que han contribuido al porte para comprimir la altura de las columnas.
457
2. 3. 4.
Abra un nuevo documento en Applix Words y despus importe el chero RTF. Imprima la Tabla de Contenidos que ya existe. Inserte guras en el documento y centre cada gura en la pgina utilizando el botn de centrado de mrgenes. No todos los documentos tienen guras. Puede buscar en los cheros fuente SGML la cadena graphic para identicar aquellas partes de la documentacin que puedan tener guras. Unas pocas guras se repiten en varias partes de la documentacin.
5. 6.
Trabaje sobre el documento ajustando saltos de pgina y anchos de columnas en las tablas. Si existe bibliografa, Applix Words aparentemente marca todo el texto restante despus del primer titulo como si tuviera un atributo de subrayado. Seleccione todo el texto restante y desactive el subrayado usando el botn de subrayado y a partir de aqu subraye explcitamente cada documento y el ttulo del libro.
458
7. 8. 9.
Siga tratando el documento marcando la Tabla de Contenidos con el correspondiente nmero de pgina para cada entrada. Reemplace los valores de nmero de pgina de la derecha en la Tabla de Contenidos con los valores correctos. Esto slo toma unos minutos por documento. Guarde el documento en el formato nativo de Applix Words para permitir una edicin rpida ms adelante si fuera necesario.
10. Imprima el documento a un chero en formato Postscript. 11. Comprima el chero Postscript utilizando gzip. Coloque el chero comprimido en el directorio doc.
DG2.7. Herramientas
Hemos documentado nuestra experiencia con tres mtodos de instalacin para las diferentes herramientas que son necesarias para procesar la documentacin. Una es la instalacin desde RPMs en Linux, la segunda es la instalacin desde el porte a FreeBSD y la ltima es una instalacin general desde distribuciones originales de las herramientas. Estas se describirn ms abajo. Pueden existir otras distribuciones empaquetadas de estas herramientas. Por favor remita informacin sobre estado de los paquetes a las listas de correo y la incluirimos aqu.
459
Instalando RPMs 1. 2. Instale los RPM (ftp://ftp.cygnus.com/pub/home/rosalia/) para Jade y los paquetes relacionados. Instale las ltimas hojas de estilo de Norm Walsh. Dependiendo de la antiguedad de los RPM, las ltimas hojas de estilo pueden haber sido muy mejoradas respecto a aquellas que aparecen con los RPM. Actualice su src/Makefile.custom para que incluyan las deniciones de HSTYLE y de PSTYLE que apuntan a las hojas de estilo.
3.
2.
460
(esto es para sintaxis sh/bash. Ajstelo para csh/tcsh). 3. Make necesita algunos argumentos especiales o estos han de ser aadidos a su Makele.custom:
HSTYLE=/usr/local/share/sgml/docbook/dsssl/modular/html/ PSTYLE=/usr/local/share/sgml/docbook/dsssl/modular/print/
Por descontado que necesitar usar gmake, no slo make, para compilar.
2.
461
a.
b.
Consiga el chero comprimido con las ltimas hojas de estilo en http://www.nwalsh.com/docbook/dsssl y descomprmalo (quizs en /usr/share).
3.
Si instala GNU m4, instlelo con el nombre gnum4 y sgmltools lo encontrar. Despus de la instalacin usted tendr sgmltools, jade y las hojas de estilo DocBook de Norman Walsh. Las instrucciones de abajo son para instalar estas herramientas de modo separado.
462
Una instalacin funcionando de GCC 2.7.2 Una instalacin trabajando de Emacs 19.19 o posterior La utilidad unzip para descomprimir cheros
Debe conseguir:
Jade de James Clark (ftp://ftp.jclark.com/pub/jade/) (la versin 1.1 en el chero jade1_1.zip en el momento de escribir esto) DocBook versin 3.0 (http://www.ora.com/davenport/docbook/current/docbk30.zip) Modular Stylesheets (http://nwalsh.com/docbook/dsssl/) de Norman Walsh (la versin 1.19 fue originalmente usada para producir estos documentos) PSGML (ftp://ftp.lysator.liu.se/pub/sgml/) de Lennar Stain (la versin 1.0.1 en psgml-1.0.1.tar.gz era la que estaba disponible en el momento de escribir esto)
URLs importantes:
La web de Jade (http://www.jclark.com/jade/) La pgina web de DocBook (http://www.ora.com/davenport/) La web de Modular Stylesheets (http://nwalsh.com/docbook/dsssl/) Web de PSGML (http://www.lysator.liu.se/projects/about_psgml.html) La gua de Steve Pepper (http://www.infotek.no/sgmltool/guide.htm) Base de datos de Robin Cover sobre software SGML (http://www.sil.org/sgml/publicSW.html)
463
3.
Jade no ha sido construido usando GNU autoconf, de modo que tendr que editar un Makefile por su cuenta. Ya que James Clark ha sido lo sucientemente bueno como para preparar su kit para ello, es una buena idea crear un directorio (con un nombre como la aquitectura de su mquina, por ejemplo) bajo el directorio principal de Jade, copiar desde l el chero Makefile al directorio recin creado, editarlo y desde ah mismo ejecutar make.
Makefile necesitar ser. Hay un chero llamado Makefile.jade en el directorio
principal cuyo cometido es ser usado con make -f Makele.jade cuando se construye Jade (a diferencia de SP, el parser SGML sobre el que est construido Jade). Aconsejamos que no se haga esto, ya que deber cambiar ms cosas que lo que hay en Makefile.jade. Recorra el chero Makefile, leyendo las instrucciones de Jame y haciendo los cambios necesarios. Hay algunas variables que necesitan ser jadas. Aqu se muestra un sumario de las ms representativas con sus valores ms tpicos:
464
Observe la especicacin de dnde encontrar el catlogo SGML por defecto de los cheros de soporte (quizs tenga que cambiarlo a algo ms adecuado para su instalacin). Si su sistema no necesita los ajustes mencionados arriba para la librera de funciones matemticas y para el comando ranlib, djelos tal y como estn en Makefile. 4. 5. Escriba make para compilar Jade y las herramientas de SP. Una vez que est compilado, make install har la instalacin.
465
2.
El chero CATALOG contendra tres tipos de lneas. La primera (opcional) la declaracion SGML :
SGMLDECL docbook.dcl
Despus, las diferentes referencias a DTD y cheros de las entidades. Para los cheros DocBook las lneas seran como estas:
PUBLIC "-//Davenport//DTD DocBook V3.0//EN" docbook.dtd PUBLIC "-//USA-DOD//DTD Table Model 951010//EN" cals-tbl.dtd PUBLIC "-//Davenport//ELEMENTS DocBook Information Pool V3.0//EN" dbpool. PUBLIC "-//Davenport//ELEMENTS DocBook Document Hierarchy V3.0//EN" dbhie PUBLIC "-//Davenport//ENTITIES DocBook Additional General Entities V3.0//EN" dbgenent.mod
3.
Por supuesto que en el kit de DocBook hay un chero que contiene todo esto. Observe que el ltimo elemento en cada una de esas lneas es un nombre de chero, que aqu se da sin el path. Puede poner los cheros en subdirectorios de su directorio SGML si quiere y modicar la referencia en el chero CATALOG. DocBook tambin referencia el conjunto de caracteres ISO de las entidades, por lo que necesitar traerlos e instalarlos (estn disponibles desde varias fuentes y se pueden encontrar fcilmente en las URLs mostradas ms arriba), adems de su entradas:
PUBLIC "ISO 8879-1986//ENTITIES Added Latin 1//EN" ISO/ISOlat1
Observe que el nombre de chero contiene un directorio, dicindonos que hemos puesto los cheros de entidades ISO en un subdirectorio ISO. Nuevamente las entradas oportunas en el catlogo deben acompaar a la entidad que se haya trado.
466
3.
Una manera de probar la instalacin es compilar los formularios HTML y RTF de la Gua de usuarios de PostgreSQL. a. Para compilar los cheros HTML vaya al directorio fuente de SGML , doc/src/sgml, y escriba
jade -t sgml -d /usr/local/share/docbook/html/docbook.dsl D ../graphics postgres.sgml
b.
Para generar el RTF preparado ser importado a su procesador de textos favorito, escriba:
jade -t rtf -d /usr/local/share/docbook/print/docbook.dsl D ../graphics postgres.sgml
467
4.
Si necesita usar PSGML cuando tambin est editando HTML aada esto:
(setq auto-mode-alist (cons ("\\.s?html?\\" . sgml-mode) auto-mode-alist))
5.
Hay una cosa importante que debe tener en cuenta con PSGML: su autor asume que su directorio principal para el DTD SGML es /usr/local/lib/sgml. Si, como en los ejemplos de captulo, utiliza /usr/local/share/sgml, debe corregirlo adecuadamente. a. b. c. Puede jar la variable de entorno SGML_CATALOG_FILES. Puede personalizar su instalacin de PSGML (el manual le dir cmo). Puede incluso editar el chero fuente psgml.el antes de compilar e instalar PSGML, de modo que cambie los path para que se adecuen a los suyos por defecto.
468
y obviamente debe poner slo los idiomas que necesite y congurar Babel para ello. Con JadeTeX en funcionamiento, debera poder generar y formatear las salidas de TeX para los manuales de PostgreSQL pasando los comandos (como ms arriba, en el directorio doc/src/sgml)
jade -t tex -d /usr/local/share/docbook/print/docbook.dsl -D ../graphics postgres.sgml jadetex postgres.tex jadetex postgres.tex dvips postgres.dvi
469
Por supuesto, cuando haga esto TeX parar durante la segunda ejecucin diciendo que su capacidad se ha sobrepasado. Esto es debido al modo en que JadeTeX genera informacin de referencias cruzadas. TeX puede ser compilado de manera que utilice estructuras de datos mayores. Los detalles de esto variarn de acuerdo a su instalacin.
470
Bibliografa
Seleccin de referencias y lecturas sobre SQL y Postgres.
Principles of Database and Knowledge : Base Systems , Ullman, 1988 , Jeffrey D. Ullman, 1, Computer Science Press , 1988 .
471
Bibliografa
The Postgres95 User Manual , Yu and Chen, 1995 , A. Yu y J. Chen, The POSTGRES Group , Sept. 5, 1995, University of California, Berkeley CA.
472
Bibliografa
Procedimientos y Articulos
Partial indexing in POSTGRES: research project , Olson, 1993 , Nels Olson, 1993, UCB Engin T7.49.1993 O676, University of California, Berkeley CA. A Unied Framework for Version Modeling Using Production Rules in a Database System , Ong and Goh, 1990 , L. Ong y J. Goh, April, 1990, ERL Technical Memorandum M90/33, University of California, Berkeley CA. The Postgres Data Model , Rowe and Stonebraker, 1987 , L. Rowe y M. Stonebraker, Sept. 1987, VLDB Conference, Brighton, England, 1987. Generalized partial indexes (http://simon.cs.cornell.edu/home/praveen/papers/partindex.de95.ps.Z) , , P. Seshadri y A. Swami, March 1995, Eleventh International Conference on Data Engineering, 1995, Cat. No.95CH35724, IEEE Computer Society Press. The Design of Postgres , Stonebraker and Rowe, 1986 , M. Stonebraker y L. Rowe, May 1986, Conference on Management of Data, Washington DC, ACM-SIGMOD, 1986. The Design of the Postgres Rules System, Stonebraker, Hanson, Hong, 1987 , M. Stonebraker, E. Hanson, y C. H. Hong, Feb. 1987, Conference on Data Engineering, Los Angeles, CA, IEEE, 1987. The Postgres Storage System , Stonebraker, 1987 , M. Stonebraker, Sept. 1987, VLDB Conference, Brighton, England, 1987. A Commentary on the Postgres Rules System , Stonebraker et al, 1989, M. Stonebraker, M. Hearst, y S. Potamianos, Sept. 1989, Record 18(3), SIGMOD, 1989. The case for partial indexes (DBMS) (http://s2k-ftp.CS.Berkeley.EDU:8000/postgres/papers/ERL-M89-17.pdf) , Stonebraker, M, 1989b, M. Stonebraker, Dec. 1989, Record 18(no.4):4-11, SIGMOD, 1989.
473
Bibliografa
The Implementation of Postgres , Stonebraker, Rowe, Hirohama, 1990 , M. Stonebraker, L. A. Rowe, y M. Hirohama, March 1990, Transactions on Knowledge and Data Engineering 2(1), IEEE. On Rules, Procedures, Caching and Views in Database Systems , Stonebraker et al, ACM, 1990 , M. Stonebraker y et al, June 1990, Conference on Management of Data, ACM-SIGMOD.
474