¿Cómo puedo determinar si existe una tabla en la ruta de búsqueda actual con PLPGSQL?


10

Estoy escribiendo un script de configuración para una aplicación que es un complemento para otra aplicación, por lo que quiero verificar si existen las tablas para la otra aplicación. Si no, quiero darle al usuario un error útil. Sin embargo, no sé qué esquema mantendrá las tablas.

DO LANGUAGE plpgsql $$
BEGIN
    PERFORM 1
    FROM
        pg_catalog.pg_class c
        JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
    WHERE
        n.nspname = current_setting('search_path')
        AND c.relname = 'foo'
        AND c.relkind = 'r'; -- not sure if I actually need this or not...

    IF NOT FOUND THEN
        RAISE 'This application depends on tables created by another application';
    END IF;
END;
$$;

Sin embargo, current_setting('search_path')devuelve un TEXTO que contiene "$user",publicpor defecto, que no es terriblemente útil.

Lo único que se me ocurre es intentar seleccionar de la tabla y detectar la excepción. Haría el trabajo, pero no creo que sea muy elegante y he leído que es costoso de usar (aunque tal vez eso estaría bien en este escenario, ya que solo lo estoy ejecutando una vez).

Respuestas:


18

Rápido y sucio

En Postgres 9.4+ uso

SELECT to_regclass('foo');

Devuelve NULL si el identificador no se encuentra en la ruta de búsqueda.
En Postgres 9.3 o anterior, usa un yeso pararegclass :

SELECT 'foo'::regclass;

¡Esto genera una excepción si no se encuentra el objeto!

Si 'foo'se encuentra, oidse devuelve en su textrepresentación. Ese es solo el nombre de la tabla, calificado por el esquema de acuerdo con la ruta de búsqueda actual y entre comillas dobles cuando sea necesario.

Si no se encuentra el objeto, puede estar seguro de que no existe en ninguna parte de la ruta de búsqueda, o que no existe para un nombre calificado con esquema ( schema.foo).

Si se encuentra hay dos deficiencias :

  1. La búsqueda incluye esquemas implícitos de search_path , a saber, pg_catalogypg_temp . Pero es posible que desee excluir las tablas temporales y del sistema para su propósito. (?)

  2. Un reparto regclasspara todos los objetos del catálogo del sistema pg_class: índices, vistas, secuencias, etc. No solo tablas. Parece que estás buscando una mesa regular exclusivamente. Sin embargo, probablemente también tenga problemas con otros objetos del mismo nombre. Detalles:

Lento y seguro

Volvemos a su consulta, pero no use current_setting('search_path'), lo que devuelve la configuración básica. Use la función de información del sistema dedicada current_schemas(). Por documentación:

current_schemas(boolean) name[]
nombres de esquemas en la ruta de búsqueda, opcionalmente incluidos esquemas implícitos

"$user"en la ruta de búsqueda se resuelve de manera inteligente. Si no existe un esquema con el nombre de SESSION_USER, el esquema no se devuelve para empezar. Además, dependiendo de lo que desee exactamente, también puede generar esquemas implícitos ( pg_catalogy posiblemente pg_temp), pero supongo que no los quiere para el caso en cuestión, así que use:

DO 
$do$
BEGIN
   IF EXISTS (
      SELECT  -- list can be empty
      FROM   pg_catalog.pg_class c
      JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
      WHERE  n.nspname = ANY(current_schemas(FALSE))
      AND    n.nspname NOT LIKE 'pg_%'  -- exclude system schemas!
      AND    c.relname = 'foo'
      AND    c.relkind = 'r')           -- you probably need this
   THEN
      RAISE 'This application depends on tables created by another application';
   END IF;
END
$do$;

SQL Fiddle , que muestra todo excepto la últimaDOdeclaración.
SQL Fiddle (JDBC) tiene problemas con las DOdeclaraciones que contienen caracteres de terminación.


1

Puede convertir el valor de configuración en una matriz y reemplazarlo $usercon el nombre de usuario actual. La matriz se puede usar en la condición where:

where n.nspname = any(string_to_array(replace(current_setting('search_path'), '$user', current_user), ','))

0
./sshi.sh vb20deployment controller <<'HERE'
export PGPASSWORD="postgres"
cd logu/postgresql/bin
row=1
tableArray=(table1 table2 table3 table4 table5 table6)

for (( x=0 ; x<=5 ; x++)) ; do        

./psql.bin --port=5432 --username=postgres --host=hostname.rds.amazonaws.com --dbname=mydb -c "SELECT * FROM information_schema.tables WHERE '${tableArray[$x]}' = table_name" | while read -a Record ; do
  row=$((row + 1))
  if [[ $row -gt 3 ]]; then

     echo ${Record[4]}

   fi
done

done


HERE
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.