Una extensión útil para PostgreSQL típicamente incluye múltiples objetos SQL;
por ejemplo, un nuevo tipo de datos requerirá nuevas funciones, nuevos operadores y probablemente nuevas
clases de operadores de índices. Es útil reunir todos estos objetos en un solo paquete para simplificar la
gestión de la base de datos. PostgreSQL llama a dicho paquete una
extensión. Para definir una extensión, necesitas al menos un archivo de
script que contenga los comandos SQL para crear los objetos de la extensión, y
un archivo de control que especifique algunas propiedades básicas de la propia
extensión. Si la extensión incluye código en C, normalmente también habrá un archivo de biblioteca compartida
en el que se ha compilado el código en C. Una vez que tienes estos archivos, un simple comando CREATE EXTENSION carga los objetos en tu base de
datos.
La ventaja principal de utilizar una extensión, en lugar de simplemente ejecutar el script
SQL para cargar un montón de objetos “sueltos” en tu base de datos, es que
entonces PostgreSQL entenderá que los objetos de la extensión van juntos.
Puedes eliminar todos los objetos con un único comando DROP EXTENSION (sin necesidad de mantener un script
de “desinstalación” separado). Aún más útil, pg_dump sabe que no
debe volcar los objetos miembros individuales de la extensión; en su lugar, simplemente incluirá un
comando CREATE EXTENSION en los volcados. Esto simplifica enormemente la migración a una
nueva versión de la extensión que podría contener más objetos o diferentes a la versión antigua. Ten en
cuenta, sin embargo, que debes tener disponibles los archivos de control, script y otros archivos de la
extensión al cargar dicho volcado en una nueva base de datos.
PostgreSQL no te permitirá eliminar un objeto individual contenido en una
extensión, excepto eliminando toda la extensión. Además, aunque puedes cambiar la definición de un objeto
miembro de la extensión (por ejemplo, a través de CREATE OR REPLACE FUNCTION para una
función), ten en cuenta que la definición modificada no será volcada por pg_dump.
Dicho cambio normalmente solo tiene sentido si realizas simultáneamente el mismo cambio en el archivo de
script de la extensión. (Pero existen disposiciones especiales para tablas que contienen datos de
configuración; consulta la sección Section 36.17.3). En situaciones de
producción, generalmente es mejor crear un script de actualización de extensión para realizar cambios en los
objetivos miembros de la extensión.
El script de la extensión puede establecer privilegios sobre los objetos que forman parte de la extensión,
utilizando sentencias GRANT y REVOKE. El conjunto final de
privilegios para cada objeto (si se establece alguno) se almacenará en el catálogo del sistema pg_init_privs. Cuando se utiliza
pg_dump, el comando CREATE EXTENSION se incluirá en el
volcado, seguido de las sentencias GRANT y REVOKE necesarias para
establecer los privilegios en los objetos a como estaban en el momento en que se realizó el volcado.
PostgreSQL no admite actualmente que los scripts de extensión emitan sentencias
CREATE POLICY o SECURITY LABEL. Se espera que se configuren después
de que se haya creado la extensión. Todas las políticas RLS y etiquetas de seguridad en los objetos de la
extensión se incluirán en los volcados creados por pg_dump.
El mecanismo de extensión también tiene disposiciones para empaquetar scripts de modificación que ajustan
las definiciones de los objetos SQL contenidos en una extensión. Por ejemplo, si la versión 1.1 de una
extensión añade una función y cambia el cuerpo de otra función en comparación con la 1.0, el autor de la
extensión puede proporcionar un script de actualización que realiza solo esos dos
cambios. El comando ALTER EXTENSION UPDATE se puede utilizar entonces para aplicar estos
cambios y realizar un seguimiento de qué versión de la extensión está instalada realmente en una base de
datos determinada.
Los tipos de objetos SQL que pueden ser miembros de una extensión se muestran en la descripción de ALTER EXTENSION. En particular, los objetos que son
a nivel de todo el clúster de la base de datos, como bases de datos, roles y tablespaces, no pueden ser
miembros de una extensión dado que una extensión solo se conoce dentro de una base de datos. (Aunque no se
prohíbe que un script de extensión cree tales objetos, si lo hace no se rastrearán como parte de la
extensión). También ten en cuenta que aunque una tabla puede ser miembro de una extensión, sus objetos
subsidiarios, como los índices, no se consideran miembros directos de la extensión. Otro punto importante
es que los esquemas pueden pertenecer a extensiones, pero no al revés: una extensión como tal tiene un
nombre no cualificado y no existe “dentro” de ningún esquema. Sin embargo, los objetos
miembros de la extensión pertenecerán a esquemas siempre que sea apropiado para sus tipos de objetos. Puede
ser o no apropiado que una extensión sea propietaria de los esquemas en los que se encuentran sus objetos
miembros.
Si el script de una extensión crea objetos temporales (como tablas temporales), esos objetos se tratan como miembros de la extensión durante el resto de la sesión actual, pero se eliminan automáticamente al final de la sesión, como ocurriría con cualquier objeto temporal. Esta es una excepción a la regla de que los objetos miembros de la extensión no se pueden eliminar sin eliminar toda la extensión.
El comando CREATE EXTENSION se basa en un archivo de control para cada extensión, que
debe llamarse igual que la extensión con el sufijo .control, y debe colocarse en el
directorio SHAREDIR/extension de la instalación. También debe haber al menos un archivo
de script SQL, que sigue el patrón de nombres
(por ejemplo, extension--version.sqlfoo--1.0.sql para la versión 1.0 de la extensión
foo). Por defecto, los archivos de script también se colocan en el directorio
SHAREDIR/extension; pero el archivo de control puede especificar un directorio
diferente para los archivos de script.
Se pueden configurar ubicaciones adicionales para los archivos de control de extensiones utilizando el parámetro extension_control_path.
El formato de archivo para un archivo de control de extensión es el mismo que para el archivo
postgresql.conf, es decir, una lista de asignaciones
parameter_name = value, una por
línea. Se permiten líneas en blanco y comentarios introducidos por #. Asegúrate de poner
entre comillas cualquier valor que no sea una sola palabra o número.
Un archivo de control puede configurar los siguientes parámetros:
directory (string) #El directorio que contiene los archivos de script SQL de la extensión. A menos que se proporcione una ruta absoluta, el nombre es relativo al directorio donde se encontró el archivo de control. Por defecto, los archivos de script se buscan en el mismo directorio donde se encontró el archivo de control.
default_version (string) #
La versión por defecto de la extensión (la que se instalará si no se especifica ninguna versión en
CREATE EXTENSION). Aunque esto se puede omitir, provocará que CREATE
EXTENSION falle si no aparece la opción VERSION, por lo que generalmente
no querrás hacer eso.
comment (string) #Un comentario (cualquier cadena) sobre la extensión. El comentario se aplica al crear inicialmente una extensión, pero no durante las actualizaciones de la extensión (dado que eso podría anular los comentarios añadidos por el usuario). Alternativamente, el comentario de la extensión se puede establecer escribiendo un comando COMMENT en el archivo de script.
encoding (string) #La codificación del conjunto de caracteres utilizada por los archivos de script. Esto debe especificarse si los archivos de script contienen caracteres no ASCII. De lo contrario, se asumirá que los archivos están en la codificación de la base de datos.
module_pathname (string) #
El valor de este parámetro se sustituirá por cada aparición de MODULE_PATHNAME en
los archivos de script. Si no está configurado, no se realiza ninguna sustitución. Típicamente, esto se
configura simplemente como y luego
se utiliza shared_library_nameMODULE_PATHNAME en los comandos CREATE FUNCTION para
funciones en lenguaje C, de modo que los archivos de script no necesiten tener cableado el nombre de
la biblioteca compartida.
requires (string) #
Una lista de nombres de extensiones de las que depende esta extensión, por ejemplo requires =
'foo, bar'. Esas extensiones deben estar instaladas antes de que esta se pueda instalar.
no_relocate (string) #
Una lista de nombres de extensiones de las que depende esta extensión a las que se les debe prohibir
cambiar sus esquemas a través de ALTER EXTENSION ... SET SCHEMA. Esto es necesario
si el script de esta extensión hace referencia al nombre del esquema de una extensión requerida
(utilizando la sintaxis @extschema:) de una manera que
no puede rastrear los cambios de nombre.
name@
superuser (boolean) #
Si este parámetro es true (que es el valor por defecto), solo los superusuarios pueden
crear la extensión o actualizarla a una nueva versión (pero consulta también trusted
a continuación). Si se configura como false, solo se requieren los privilegios necesarios
para ejecutar los comandos en el script de instalación o actualización. Esto normalmente debería
configurarse como true si alguno de los comandos del script requiere privilegios de
superuser. (Tales comandos fallarían de todos modos, pero es más amigable para el usuario dar el error
de antemano).
trusted (boolean) #
Este parámetro, si se configura como true (que no es el valor por defecto), permite
a algunos no superusuarios instalar una extensión que tiene superuser configurado
como true. Específicamente, la instalación estará permitida para cualquiera que
tenga el privilegio CREATE en la base de datos actual. Cuando el usuario que ejecuta
CREATE EXTENSION no es un superusuario pero se le permite instalar por virtud de
este parámetro, entonces el script de instalación o actualización se ejecuta como el superusuario de
arranque, no como el usuario llamador. Este parámetro es irrelevante si superuser
es false. Generalmente, esto no debería configurarse como true para extensiones
que podrían permitir el acceso a capacidades que de otro modo serían exclusivas de superusuario, como
el acceso al sistema de archivos. Además, marcar una extensión como confiable requiere un esfuerzo
adicional significativo para escribir los scripts de instalación y actualización de la extensión de
forma segura; consulta la sección Section 36.17.6.
relocatable (boolean) #
Una extensión es reubicable (relocatable) si es posible mover sus objetos
contenidos a un esquema diferente después de la creación inicial de la extensión. El valor por defecto
es false, es decir, la extensión no es reubicable. Consulta la sección Section 36.17.2 para obtener más información.
schema (string) #
Este parámetro solo se puede configurar para extensiones no reubicables. Fuerza a que la extensión se
cargue exactamente en el esquema nombrado y no en otro. El parámetro schema se consulta
solo al crear inicialmente una extensión, no durante las actualizaciones de la misma. Consulta la
sección Section 36.17.2 para obtener más información.
Además del archivo de control principal , una
extensión puede tener archivos de control secundarios nombrados al estilo
extension.control. Si se
proporcionan, deben ubicarse en el directorio del archivo de script. Los archivos de control secundarios
siguen el mismo formato que el archivo de control principal. Cualquier parámetro configurado en un archivo
de control secundario anula al archivo de control principal al instalar o actualizar a esa versión de la
extensión. Sin embargo, los parámetros extension--version.controldirectory y default_version
no se pueden configurar en un archivo de control secundario.
Los archivos de script SQL de una extensión pueden contener cualquier comando SQL, excepto
comandos de control de transacciones (BEGIN, COMMIT, etc.) y comandos
que no se pueden ejecutar dentro de un bloque de transacción (como VACUUM). Esto se debe
a que los archivos de script se ejecutan implícitamente dentro de un bloque de transacción.
Los archivos de script SQL de una extensión también pueden contener líneas que comiencen
con \echo, las cuales serán ignoradas (tratadas como comentarios) por el mecanismo de
extensión. Esta disposición se utiliza comúnmente para lanzar un error si el archivo de script se alimenta
a psql en lugar de cargarse a través de CREATE EXTENSION (ver
el script de ejemplo en Section 36.17.7). Sin eso, los usuarios podrían cargar
accidentalmente el contenido de la extensión como objetos “sueltos” en lugar de como una
extensión, una situación de la que es un poco tedioso recuperarse.
Si el script de la extensión contiene la cadena @extowner@, esa cadena se reemplaza por
el nombre (debidamente citado) del usuario que llama a CREATE EXTENSION o ALTER
EXTENSION. Típicamente, esta característica es utilizada por extensiones que están marcadas como
confiables para asignar la propiedad de los objetos seleccionados al usuario llamador en lugar de al
superusuario de arranque. (Sin embargo, se debe tener cuidado al hacerlo. Por ejemplo, asignar la propiedad
de una función en lenguaje C a un no superusuario crearía una ruta de escalada de privilegios para ese
usuario).
Aunque los archivos de script pueden contener cualquier carácter permitido por la codificación especificada,
los archivos de control solo deben contener ASCII simple, porque no hay forma de que
PostgreSQL sepa en qué codificación está un archivo de control. En la práctica,
esto solo es un problema si deseas utilizar caracteres no ASCII en el comentario de la extensión. La práctica
recomendada en ese caso es no utilizar el parámetro comment del archivo de control, sino
utilizar COMMENT ON EXTENSION dentro de un archivo de script para establecer el comentario.
Los usuarios a menudo desean cargar los objetos contenidos en una extensión en un esquema diferente al que el autor de la extensión tenía en mente. Existen tres niveles de reubicación admitidos:
Una extensión totalmente reubicable se puede mover a otro esquema en cualquier momento, incluso después
de haber sido cargada en una base de datos. Esto se realiza con el comando ALTER EXTENSION SET
SCHEMA, que renombra automáticamente todos los objetos miembros al nuevo esquema. Normalmente,
esto solo es posible si la extensión no contiene suposiciones internas sobre en qué esquema se encuentra
alguno de sus objetos. Además, los objetos de la extensión deben estar todos en un esquema para empezar
(ignorando los objetos que no pertenecen a ningún esquema, como los lenguajes procedimentales). Marca una
extensión totalmente reubicable configurando relocatable = true en su archivo de
control.
Una extensión podría ser reubicable durante la instalación pero no después. Este es típicamente el caso
si el archivo de script de la extensión necesita hacer referencia al esquema de destino explícitamente,
por ejemplo al establecer propiedades de search_path para funciones SQL. Para tal
extensión, configura relocatable = false en su archivo de control, y utiliza
@extschema@ para referirse al esquema de destino en el archivo de script. Todas las
apariciones de esta cadena se reemplazarán por el nombre real del esquema de destino (entre comillas dobles
si es necesario) antes de que se ejecute el script. El usuario puede establecer el esquema de destino
utilizando la opción SCHEMA de CREATE EXTENSION.
Si la extensión no admite la reubicación en absoluto, configura relocatable = false
en su archivo de control, y también establece schema con el nombre del esquema de
destino previsto. Esto evitará el uso de la opción SCHEMA de CREATE
EXTENSION, a menos que especifique el mismo esquema nombrado en el archivo de control. Esta
opción es típicamente necesaria si la extensión contiene suposiciones internas sobre su nombre de esquema
que no se pueden reemplazar por usos de @extschema@. El mecanismo de sustitución
@extschema@ también está disponible en este caso, aunque es de uso limitado dado que
el nombre del esquema está determinado por el archivo de control.
En todos los casos, el archivo de script se ejecutará con search_path inicialmente
configurado para apuntar al esquema de destino; es decir, CREATE EXTENSION hace lo
equivalente a esto:
SET LOCAL search_path TO @extschema@, pg_temp;
Esto permite que los objetos creados por el archivo de script vayan al esquema de destino. El archivo de
script puede cambiar el search_path si lo desea, pero eso generalmente es indeseable. El
search_path se restaura a su configuración anterior al finalizar CREATE
EXTENSION.
El esquema de destino está determinado por el parámetro schema en el archivo de control
si se proporciona, de lo contrario por la opción SCHEMA de CREATE EXTENSION
si se proporciona, de lo contrario por el esquema de creación de objetos predeterminado actual (el primero
en el search_path del llamador). Cuando se utiliza el parámetro schema
del archivo de control, el esquema de destino se creará si no existe ya, pero en los otros dos casos ya
debe existir.
Si se enumeran extensiones prerrequisito en requires en el archivo de control, sus
esquemas de destino se añaden a la configuración inicial de search_path, después del
esquema de destino de la nueva extensión. Esto permite que sus objetos sean visibles para el archivo de
script de la nueva extensión.
Por seguridad, pg_temp se añade automáticamente al final de search_path
en todos los casos.
Aunque una extensión no reubicable puede contener objetos distribuidos en múltiples esquemas, normalmente
es deseable colocar todos los objetos destinados al uso externo en un solo esquema, que se considera el
esquema de destino de la extensión. Tal disposición funciona convenientemente con la configuración
predeterminada de search_path durante la creación de extensiones dependientes.
Si una extensión hace referencia a objetos que pertenecen a otra extensión, se recomienda calificar con el
esquema esas referencias. Para hacerlo, escribe @extschema:
in el archivo de script de la extensión, donde name@name es el nombre de la otra
extensión (que debe figurar en la lista requires de esta extensión). Esta cadena se
reemplazará por el nombre (entre comillas dobles si es necesario) del esquema de destino de esa extensión.
Aunque esta notación evita la necesidad de hacer suposiciones cableadas sobre los nombres de los esquemas en
el archivo de script de la extensión, su uso puede incrustar el nombre del esquema de la otra extensión
en los objetos instalados de esta extensión. (Típicamente, eso sucede cuando
@extschema: se utiliza dentro de un literal de cadena,
como el cuerpo de una función o una configuración de name@search_path. En otros casos, la
referencia al objeto se reduce a un OID durante el análisis y no requiere búsquedas posteriores). Si el
nombre del esquema de la otra extensión está así incrustado, debes evitar que la otra extensión se reubique
después de instalar la tuya, añadiendo el nombre de la otra extensión a la lista no_relocate
de esta.
Algunas extensiones incluyen tablas de configuración, que contienen datos que el usuario podría añadir o cambiar después de la instalación de la extensión. Normalmente, si una tabla es parte de una extensión, ni la definición de la tabla ni su contenido serán volcados por pg_dump. Pero ese comportamiento es indeseable para una tabla de configuración; cualquier cambio de datos realizado por el usuario debe incluirse en los volcados, o la extensión se comportará de manera diferente después de un volcado y restauración.
Para resolver este problema, el archivo de script de una extensión puede marcar una tabla o una secuencia
que ha creado como una relación de configuración, lo que hará que pg_dump incluya
el contenido de la tabla o de la secuencia (no su definición) en los volcados. Para hacer eso, llama a la
función pg_extension_config_dump(regclass, text) después de crear la tabla o la
secuencia, por ejemplo:
CREATE TABLE my_config (key text, value text);
CREATE SEQUENCE my_config_seq;
SELECT pg_catalog.pg_extension_config_dump('my_config', '');
SELECT pg_catalog.pg_extension_config_dump('my_config_seq', '');
Se puede marcar cualquier número de tablas o secuencias de esta manera. Las secuencias asociadas con columnas
serial o bigserial también se pueden marcar.
Cuando el segundo argumento de pg_extension_config_dump es una cadena vacía, todo el
contenido de la tabla es volcado por pg_dump. Esto normalmente solo es correcto
si la tabla está inicialmente vacía tal como la creó el script de la extensión. Si hay una mezcla de datos
iniciales y datos proporcionados por el usuario en la tabla, el segundo argumento de
pg_extension_config_dump proporciona una condición WHERE que
selecciona los datos a volcar. Por ejemplo, podrías hacer:
CREATE TABLE my_config (key text, value text, standard_entry boolean);
SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');
y luego asegurarte de que standard_entry sea true solo en las filas creadas por
el script de la extensión.
Para las secuencias, el segundo argumento de pg_extension_config_dump no tiene efecto.
Las situaciones más complicadas, como las filas proporcionadas inicialmente que podrían ser modificadas por los usuarios, se pueden manejar creando disparadores (triggers) en la tabla de configuración para asegurar que las filas modificadas se marquen correctamente.
Puedes alterar la condición de filtro asociada con una tabla de configuración llamando a
pg_extension_config_dump nuevamente. (Esto normalmente sería útil en un script de
actualización de extensión). La única forma de marcar una tabla como que ya no es una tabla de configuración
es desasociarla de la extensión con ALTER EXTENSION ... DROP TABLE.
Ten en cuenta que las relaciones de clave externa entre estas tablas dictarán el orden en el que pg_dump las volcará. Específicamente, pg_dump intentará volcar la tabla referenciada antes que la tabla que la referencia. Como las relaciones de clave externa se configuran en el momento de CREATE EXTENSION (antes de que se carguen los datos en las tablas), no se admiten dependencias circulares. Cuando existen dependencias circulares, los datos se seguirán volcando pero el volcado no se podrá restaurar directamente y se requerirá la intervención del usuario.
Las secuencias asociadas con columnas serial o bigserial deben marcarse directamente
para volcar su estado. Marcar su relación padre no es suficiente para este propósito.
Una ventaja del mecanismo de extensión es que proporciona formas convenientes de gestionar las actualizaciones
de los comandos SQL que definen los objetos de una extensión. Esto se realiza asociando un nombre o número
de versión con cada versión lanzada del script de instalación de la extensión. Además, si deseas que los
usuarios puedan actualizar sus bases de datos dinámicamente de una versión a la siguiente, debes proporcionar
scripts de actualización que realicen los cambios necesarios para pasar de una versión
a la siguiente. Los scripts de actualización tienen nombres que siguen el patrón
(por ejemplo, extension--old_version--target_version.sqlfoo--1.0--1.1.sql contiene los comandos para modificar la versión
1.0 de la extensión foo a la versión 1.1).
Dado que está disponible un script de actualización adecuado, el comando ALTER EXTENSION UPDATE
actualizará una extensión instalada a la nueva versión especificada. El script de actualización se ejecuta
en el mismo entorno que proporciona CREATE EXTENSION para los scripts de instalación: en
particular, el search_path se configura de la misma manera, y cualquier objeto nuevo
creado por el script se añade automáticamente a la extensión. Además, si el script decide eliminar objetos
miembros de la extensión, estos se desasocian automáticamente de la extensión.
Si una extensión tiene archivos de control secundarios, los parámetros de control que se utilizan para un script de actualización son los asociados con la versión de destino (nueva) del script.
ALTER EXTENSION puede ejecutar secuencias de archivos de scripts de actualización para
lograr una actualización solicitada. Por ejemplo, si solo están disponibles foo--1.0--1.1.sql
y foo--1.1--2.0.sql, ALTER EXTENSION los aplicará en secuencia si se
solicita una actualización a la versión 2.0 cuando 1.0 está instalada
actualmente.
PostgreSQL no asume nada sobre las propiedades de los nombres de las versiones:
por ejemplo, no sabe si 1.1 sigue a 1.0. Simplemente hace coincidir
los nombres de versión disponibles y sigue la ruta que requiere aplicar el menor número de scripts de
actualización. (Un nombre de versión puede ser en realidad cualquier cadena que no contenga --
ni un - inicial o final).
A veces es útil proporcionar scripts de “retroceso” (downgrade), por ejemplo
foo--1.1--1.0.sql para permitir revertir los cambios asociados con la versión
1.1. Si haces eso, ten cuidado con la posibilidad de que se aplique inesperadamente un
script de retroceso porque produce una ruta más corta. El caso de riesgo es donde existe un script de
actualización de “ruta rápida” que salta varias versiones, así como un script de retroceso
al punto de inicio de la ruta rápida. Podría requerir menos pasos aplicar el retroceso y luego la ruta rápida
que avanzar una versión a la vez. Si el script de retroceso elimina objetos irremplazables, esto producirá
resultados indeseables.
Para verificar rutas de actualización inesperadas, utiliza este comando:
SELECT * FROM pg_extension_update_paths('extension_name');
Esto muestra cada par de nombres de versión conocidos distintos para la extensión especificada, junto con la
secuencia de la ruta de actualización que se tomaría para ir de la versión de origen a la versión de destino,
o NULL si no hay una ruta de actualización disponible. La ruta se muestra en formato de
texto con separadores --. Puedes utilizar regexp_split_to_array(path,'--')
si prefieres un formato de array.
Una extensión que ha existido durante algún tiempo probablemente existirá en varias versiones, para las cuales
el autor necesitará escribir scripts de actualización. Por ejemplo, si has lanzado una extensión
foo en las versiones 1.0, 1.1 y 1.2,
debería haber scripts de actualización foo--1.0--1.1.sql y
foo--1.1--1.2.sql. Antes de PostgreSQL 10, era necesario
crear también nuevos archivos de script foo--1.1.sql y foo--1.2.sql
que construyeran directamente las versiones más recientes de la extensión, o de lo contrario las versiones
más recientes no se podían instalar directamente, sino únicamente instalando la 1.0 y
luego actualizando. Eso era tedioso y duplicado, pero ahora es innecesario, porque CREATE
EXTENSION puede seguir las cadenas de actualización automáticamente. Por ejemplo, si solo están
disponibles los archivos de script foo--1.0.sql, foo--1.0--1.1.sql
y foo--1.1--1.2.sql, entonces una solicitud para instalar la versión 1.2
se cumple ejecutando esos tres scripts en secuencia. El procesamiento es el mismo que si hubieras instalado
primero la 1.0 y luego hubieras actualizado a la 1.2. (Al igual que con
ALTER EXTENSION UPDATE, si hay múltiples rutas disponibles, se prefiere la más corta).
Organizar los archivos de script de una extensión de este estilo puede reducir la cantidad de esfuerzo de
mantenimiento necesario para producir pequeñas actualizaciones.
Si utilizas archivos de control secundarios (específicos de la versión) con una extensión mantenida en este
estilo, ten en cuenta que cada versión necesita un archivo de control incluso si no tiene un script de
instalación independiente, ya que ese archivo de control determinará cómo se realiza la actualización
implícita a esa versión. Por ejemplo, si foo--1.0.control especifica requires
= 'bar' pero los otros archivos de control de foo no lo hacen, la dependencia de
la extensión sobre bar se eliminará al actualizar de la 1.0 a otra versión.
Las extensiones ampliamente distribuidas deben asumir poco sobre la base de datos que ocupan. Por lo tanto, es apropiado escribir las funciones proporcionadas por una extensión en un estilo seguro que no pueda ser comprometido por ataques basados en search-path.
Una extensión que tiene la propiedad superuser establecida en true también debe considerar
los riesgos de seguridad para las acciones realizadas dentro de sus scripts de instalación y actualización.
No es terriblemente difícil para un usuario malicioso crear objetos caballo de Troya que comprometerán la
ejecución posterior de un script de extensión escrito descuidadamente, permitiendo a ese usuario adquirir
privilegios de superusuario.
Si una extensión está marcada como trusted, entonces su esquema de instalación puede ser
seleccionado por el usuario instalador, quien podría utilizar intencionadamente un esquema inseguro con la
esperanza de obtener privilegios de superusuario. Por lo tanto, una extensión de confianza está extremadamente
expuesta desde el punto de vista de la seguridad, y todos los comandos de sus scripts deben ser examinados
cuidadosamente para asegurar que ningún compromiso sea posible.
Los consejos sobre cómo escribir funciones de forma segura se proporcionan en la sección Section 36.17.6.1 a continuación, y los consejos sobre cómo escribir scripts de instalación de forma segura se proporcionan en la sección Section 36.17.6.2.
Las funciones en lenguaje SQL y PL proporcionadas por las extensiones corren el riesgo de sufrir ataques basados en search-path cuando se ejecutan, dado que el análisis de estas funciones ocurre en el momento de la ejecución, no en el de la creación.
La página de referencia de CREATE FUNCTION
contiene consejos sobre cómo escribir funciones SECURITY DEFINER de forma segura. Es una buena
práctica aplicar esas técnicas para cualquier función proporcionada por una extensión, ya que la función podría
ser llamada por un usuario con altos privilegios.
Si no puedes configurar el search_path para que contenga únicamente esquemas seguros, asume
que cada nombre no cualificado podría resolverse a un objeto que un usuario malicioso ha definido. Ten cuidado
con las construcciones que dependen implícitamente de search_path; por ejemplo, IN
y CASE siempre seleccionan un operador utilizando
el search path. En su lugar, utiliza expression WHENOPERATOR( y
schema.=) ANYCASE WHEN .
expression
Una extensión de propósito general normalmente no debería asumir que ha sido instalada en un esquema seguro,
lo que significa que incluso las referencias cualificadas por esquema a sus propios objetos no están completamente
libres de riesgo. Por ejemplo, si la extensión ha definido una función myschema.myfunc(bigint),
entonces una llamada como myschema.myfunc(42) podría ser capturada por una función hostil
myschema.myfunc(integer). Ten cuidado de que los tipos de datos de los parámetros de funciones
y operadores coincidan exactamente con los tipos de argumentos declarados, utilizando conversiones explícitas
donde sea necesario.
Un script de instalación o actualización de extensión debe escribirse para protegerse contra los ataques basados en search-path que ocurren cuando se ejecuta el script. Si se puede hacer que una referencia a un objeto en el script se resuelva a algún otro objeto diferente al que el autor del script pretendía, entonces podría ocurrir un compromiso de inmediato, o más tarde cuando se utilice el objeto de extensión mal definido.
Los comandos DDL como CREATE FUNCTION y CREATE OPERATOR CLASS son generalmente
seguros, pero ten cuidado con cualquier comando que tenga una expresión de propósito general como componente.
Por ejemplo, CREATE VIEW necesita ser examinado, al igual que una expresión DEFAULT
en CREATE FUNCTION.
A veces, el script de una extensión puede necesitar ejecutar SQL de propósito general, por ejemplo para realizar
ajustes de catálogo que no son posibles a través de DDL. Ten cuidado de ejecutar tales comandos con un
search_path seguro; no confíes en que la ruta proporcionada por
CREATE/ALTER EXTENSION sea segura. La mejor práctica es configurar temporalmente
search_path como pg_catalog, pg_temp e insertar referencias al esquema de
instalación de la extensión explícitamente donde sea necesario. (Esta práctica también podría ser útil para
crear vistas). Se pueden encontrar ejemplos en los módulos contrib en la distribución del
código fuente de PostgreSQL.
Las referencias seguras entre extensiones normalmente requieren calificar con el esquema los nombres de los objetos
de la otra extensión, utilizando la sintaxis @extschema:, además
de una coincidencia cuidadosa de los tipos de argumentos para funciones y operadores.
name@
Aquí hay un ejemplo completo de una extensión únicamente de SQL, un tipo compuesto de dos elementos que puede almacenar cualquier tipo de valor en sus ranuras (slots), las cuales se denominan “k” y “v”. Los valores que no son de texto se fuerzan automáticamente a texto para su almacenamiento.
El archivo de script pair--1.0.sql se ve así:
-- quejarse si el script se genera en psql, en lugar de a través de CREATE EXTENSION \echo Use "CREATE EXTENSION pair" to load this file. \quit CREATE TYPE pair AS ( k text, v text ); CREATE FUNCTION pair(text, text) RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@[email protected];'; CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, FUNCTION = pair); -- "SET search_path" es fácil de hacer bien, pero los nombres cualificados funcionan mejor. CREATE FUNCTION lower(pair) RETURNS pair LANGUAGE SQL AS 'SELECT ROW(lower($1.k), lower($1.v))::@[email protected];' SET search_path = pg_temp; CREATE FUNCTION pair_concat(pair, pair) RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k, $1.v OPERATOR(pg_catalog.||) $2.v)::@[email protected];';
El archivo de control pair.control se ve así:
# extensión pair comment = 'A key/value pair data type' default_version = '1.0' # no puede ser reubicable debido al uso de @extschema@ relocatable = false
Aunque apenas necesitas un archivo makefile para instalar estos dos archivos en el directorio correcto, podrías utilizar
un Makefile que contenga esto:
EXTENSION = pair DATA = pair--1.0.sql PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS)
Este makefile se basa en PGXS, que se describe en la sección Section 36.18. El comando
make install instalará los archivos de control y de script en el directorio correcto según lo informado
por pg_config.
Una vez instalados los archivos, utiliza el comando CREATE EXTENSION para cargar los objetos en
cualquier base de datos en particular.