Reglas frente triggers

Son muchas las cosas que se hacen utilizando triggers que pueden hacerse también utilizando el sistema de las reglas de Postgres. Lo que actualmente no se puede implementar a través de reglas son algunos tipos de restricciones (constraints). Es posible situar una regla cualificada que reescriba una query a NOTHING si el valor de la columna no aparece en otra tabla, pero entonces los datos son eliminados silenciosamente, y eso no es una buena idea. Si se necesitan comprobaciones para valores válidos, y en el caso de aparecer un valor inválido dar un mensaje de error, eso deberá hacerse por ahora con un trigger.

Por otro lado, un trigger que se dispare a partir de una INSERT en una vista puede hacer lo mismo que una regla, situar los datos en cualquier otro sitio y suprimir la inserción en una vista. Pero no puede hacer lo mismo en una UPDATE o una DELETE, poruqe no hay datos reales en la relación vista que puedan ser comprobados, y por ello el trigger nunca podría ser llamado. Sólo una regla podría ayudarnos.

Para los tratamientos que podrían implementarse de ambas formas, dependerá del uso de la base de datos cuál sea la mejor. Un trigger se dispara para cada fila afectada. Una regla manipula el árbol de traducción o genera uno adicional. De modo que si se manupulan muchas filas en una instrucción, una regla ordenando una query adicional usualmente daría un mejor resultado que un trigger que se llama para cada fila individual y deberá ejecutar sus operaciones muchas veces.

Por ejemplo: hay dos tablas.

    CREATE TABLE computer (
        hostname        text     -- indexed
	manufacturer    text     -- indexed
    );

    CREATE TABLE software (
        software        text,    -- indexed
        hostname        text     -- indexed
    );
Ambas tablas tienen muchos millares de filas y el índice sobre hostname es único. La columna hostname contiene el nombre de dominio cualificado completo del ordenador. La regla/trigger debería desencadenar el borrado de filas de la tabla software que se refieran a un host borrado. Toda vez que el trigger se llama para cada fila individual borrada de computer, se puede usar la instrucción
    DELETE FROM software WHERE hostname = $1;
en un plan preparado y salvado, y pasar el hostname en el parámetro. La regla debería 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 (rápido) y la query lanzada por el trigger también debería ser un barrido de índice (rápido también). La query extra para la regla sería 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
De modo que no habría mucha diferencia de velocidad entre la implementación 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'
Donde el plan de ejecución para la query de la regla será
    Hash Join
      ->  Seq Scan on software
      ->  Hash
	    ->  Index Scan using comp_hostidx on computer
La otra query posible es
    DELETE FROM computer WHERE hostname ~ '^old';
con un plan de ejecución
    Nestloop
      ->  Index Scan using comp_hostidx on computer
      ->  Index Scan using soft_hostidx on software
Esto muestra que el optimizador no comprueba que la cualificación sobre hostname en computer también debería se utilizado para un barrido por índice en software donde hay múltiples expresiones de cualificación combinadas con AND, que el hace en la versión regexp de la query. El trigger será invocado una vez para cada una de los 2000 viejos ordenadores que serán borrados, lo que dará como resultado un barrido por índice sobre computer y 2000 barridos por índice sobre software. La implementación de la regla lo hará con dos queries sobre índices. Y dependerá del tamaño promedio de la tabla software si la regla será más rápida en una situación 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 memoría caché.

La última query que veremos es

    DELETE FROM computer WHERE manufacurer = 'bim';
De nuevo esto debería dar como resultado muchoas filas 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. Sólo usando otro índice en computer:
    Nestloop
      ->  Index Scan using comp_manufidx on computer
      ->  Index Scan using soft_hostidx on software
dando como resultado de la query de las reglas
    DELETE FROM software WHERE computer.manufacurer = 'bim'
                           AND software.hostname = computer.hostname;
En cualquiera de estos casos, las queries extra del sistema de reglas serán más o menos independientes del número de filas afectadas en la query.

Otra situación son los casos de UPDATE donde depende del cambio de un atributo si la acción debería realizarse o no. En la versión 6.4 de Postgres, la especificación 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 cualficación 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 optimización de un trigger deberá fallar por definición en este caso, porque el hecjo de que su accoión solo se hará cuando un atributo específico sea actualizado, está oculto a su funcionalidad. La definición de un trigger sólo permite especificar el nivel de fila, de modo que si se toca una fila, el trigger será llamado a hacer su trabajo. El sistema de reglas lo sabrá mirándo la lista objetivo y suprimirá la query adicional por completo si el atributo no se ha tocado. De modo que la regla, cualificada o no, sólo hará sus barridos si tiene algo que hacer.

Las reglas sólo serán significativamente más lentas que los triggers si sus acciones dan como resultado joins grandes y mal cualificadas, una situación en la que falla el optimizador. Tenemos un gran martillo. Utilizar un gran martillo sin cuidado puede causar un gran daño, pero dar el toque correcto, puede hundir cualquier clavo hasta la cabeza.