sql >> Base de Datos >  >> RDS >> PostgreSQL

Usar la salida de texto de una función como nueva consulta

El truco con PREPARE no funciona, ya que no toma una * cadena de texto* (un valor) como CREATE FUNCTION lo hace, pero una declaración válida (código).

Para convertir datos en código ejecutable necesita usar SQL dinámico, es decir, EXECUTE en una función plpgsql o DO declaración. Esto funciona sin problemas siempre que el tipo de retorno no dependa del resultado de la primera función myresult() . De lo contrario, volverá a atrapar 22 como se describe en mi respuesta anterior:

  • Cómo ejecutar un resultado de cadena de un procedimiento almacenado en postgres

La parte crucial es declarar el tipo de devolución (tipo de fila en este caso) de alguna manera. Puedes crear una TABLE , TEMP TABLE o TYPE con el propósito. O puede usar una declaración preparada o un cursor de referencia.

Solución con declaración preparada

Has estado muy cerca. La pieza que falta del rompecabezas es preparar la consulta generada con SQL dinámico .

Función para preparar declaraciones dinámicamente

Cree esta función una vez . Es una versión optimizada y segura de su función myresult() :

CREATE OR REPLACE FUNCTION f_prep_query (_tbl regclass, _prefix text)
  RETURNS void AS 
$func$
BEGIN
   IF EXISTS (SELECT 1 FROM pg_prepared_statements WHERE name = 'stmt_dyn') THEN
      DEALLOCATE stmt_dyn;
   END IF;                 -- you my or may not need this safety check 

   EXECUTE (
     SELECT 'PREPARE stmt_dyn AS SELECT '
         || string_agg(quote_ident(attname), ',' ORDER BY attname)
         || ' FROM ' || _tbl
      FROM   pg_catalog.pg_attribute
      WHERE  attrelid = _tbl
      AND    attname LIKE _prefix || '%'
      AND    attnum > 0
      AND    NOT attisdropped
     );
END
$func$  LANGUAGE plpgsql;

Yo uso regclass para el parámetro de nombre de tabla _tbl para que sea inequívoco y seguro contra SQLi. Detalles:

  • Nombre de tabla como parámetro de función de PostgreSQL

El esquema de información no incluye la columna oid de los catálogos del sistema, así que cambié a pg_catalog.pg_attribute en lugar de information_schema.columns . Eso es más rápido, también. Hay ventajas y desventajas para esto:

  • Cómo verificar si una tabla existe en un esquema dado

Si una sentencia preparada con el nombre stmt_dyn ya existía, PREPARE plantearía una excepción. Si eso es aceptable, elimine la marca en la vista del sistema pg_prepared_statements y el siguiente DEALLOCATE .
Son posibles algoritmos más sofisticados para administrar múltiples declaraciones preparadas por sesión, o tomar el nombre de la declaración preparada como parámetro adicional, o incluso usar un hash MD5 de la cadena de consulta como nombre, pero eso está más allá del alcance de esta pregunta.

Tenga en cuenta que PREPARE opera fuera del alcance de las transacciones , una vez PREPARE tiene éxito, la instrucción preparada existe durante la duración de la sesión. Si se aborta la transacción de envoltura, PREPARE no se ve afectado ROLLBACK no puedo eliminar declaraciones preparadas.

Ejecución dinámica de consultas

Dos consultas, pero solo una llamar al servidor. Y muy eficiente, también.

SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;

Más simple y mucho más eficiente para la mayoría de los casos de uso simples que crear una tabla temporal o un cursor y seleccionar/obtener desde eso (que serían otras opciones).

Violín SQL.