Como posso determinar se existe uma tabela no atual search_path com o PLPGSQL?

10

Estou escrevendo um script de instalação para um aplicativo que é um complemento para outro aplicativo, então quero verificar se as tabelas para o outro aplicativo existem. Caso contrário, quero dar ao usuário um erro útil. No entanto, não sei qual esquema manterá as tabelas.

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;
$$;

No entanto, current_setting('search_path')retorna um texto contendo "$user",publicpor padrão, o que não é muito útil.

A única outra coisa que consigo pensar é tentar selecionar da tabela e capturar a exceção. Seria bom, mas não acho muito elegante e li que é caro de usar (embora talvez seja bom nesse cenário, já que estou executando apenas uma vez?).

cimmanon
fonte

Respostas:

18

Rapido e sujo

No Postgres 9.4 ou superior, use

SELECT to_regclass('foo');

Retorna NULL se o identificador não for encontrado no caminho de pesquisa.
No Postgres 9.3 ou anterior, use um elenco pararegclass :

SELECT 'foo'::regclass;

Isso gera uma exceção , se o objeto não for encontrado!

Se 'foo'for encontrado, o oidé retornado em sua textrepresentação. Esse é apenas o nome da tabela, qualificado pelo esquema de acordo com o caminho de pesquisa atual e aspas duplas, quando necessário.

Se o objeto não for encontrado, você pode ter certeza de que ele não existe em nenhum lugar no caminho de pesquisa - ou não existe um nome qualificado para o esquema ( schema.foo).

Se for encontrado, existem duas deficiências :

  1. A pesquisa inclui esquemas implícitos do search_path , ou seja, pg_catalogepg_temp . Mas você pode excluir tabelas temporárias e do sistema para seu propósito. (?)

  2. Uma conversão regclasspara todos os objetos do catálogo do sistema pg_class: índices, visualizações, sequências etc. Não apenas tabelas. Você parece estar procurando uma mesa regular exclusivamente. No entanto, você provavelmente também terá problemas com outros objetos com o mesmo nome. Detalhes:

Lento e seguro

Voltamos à sua consulta, mas não a usamos current_setting('search_path'), que retorna a configuração vazia. Use a função de informações do sistema dedicada current_schemas(). Por documentação:

current_schemas(boolean) name[]
nomes de esquemas no caminho de pesquisa, incluindo opcionalmente esquemas implícitos

"$user"no caminho de pesquisa é resolvido de forma inteligente. Se não existir um esquema com o nome de SESSION_USER, o esquema não será retornado para começar. Além disso, dependendo do que você deseja exatamente, você pode extrair esquemas implícitos ( pg_cataloge possivelmente pg_temp) - mas presumo que você não os queira para o caso em questão, 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 , demonstrando tudo, exceto a últimaDOinstrução.
O SQL Fiddle (JDBC) tem problemas com DOinstruções que contêm caracteres de finalização.

Erwin Brandstetter
fonte
1

Você pode converter o valor de configuração em uma matriz e substituí-lo pelo $usernome de usuário atual. A matriz pode então ser usada na condição where:

where n.nspname = any(string_to_array(replace(current_setting('search_path'), '$user', current_user), ','))
um cavalo sem nome
fonte
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
Vishal Bendre
fonte