

 O Amazon Redshift não permitirá mais a criação de UDFs do Python a partir do Patch 198. As UDFs do Python existentes continuarão a funcionar normalmente até 30 de junho de 2026. Para ter mais informações, consulte a [publicação de blog ](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/). 

# Criar procedimentos armazenados no Amazon Redshift
<a name="stored-procedure-overview"></a>

Este tópico descreve como criar e usar procedimentos armazenados no Amazon Redshift. Um procedimento armazenado é uma coleção de instruções SQL que vários programas podem usar.

Você pode definir um procedimento armazenado do Amazon Redshift usando a linguagem procedural do PostgreSQL PL/pgSQL para realizar um conjunto de consultas SQL e operações lógicas. O procedimento é armazenado no banco de dados e está disponível para qualquer usuário com privilégios de banco de dados suficientes. 

Ao contrário de uma função definida pelo usuário (UDF), um procedimento armazenado pode incorporar linguagem de definição de dados (DDL) e linguagem de manipulação de dados (DML) além de consultas SELECT. Um procedimento armazenado não precisa retornar um valor. Use a linguagem processual, incluindo loops e expressões condicionais, para controlar o fluxo lógico. 

Para obter detalhes sobre os comandos do SQL para criar e gerenciar procedimentos armazenados, consulte os seguintes tópicos de comandos:
+ [CREATE PROCEDURE](r_CREATE_PROCEDURE.md)
+ [ALTER PROCEDURE](r_ALTER_PROCEDURE.md)
+ [DROP PROCEDURE](r_DROP_PROCEDURE.md)
+ [SHOW PROCEDURE](r_SHOW_PROCEDURE.md)
+ [CALL](r_CALL_procedure.md)
+ [GRANT](r_GRANT.md)
+ [REVOKE](r_REVOKE.md)
+ [ALTER DEFAULT PRIVILEGES](r_ALTER_DEFAULT_PRIVILEGES.md)

**Topics**
+ [Visão geral dos procedimentos armazenados no Amazon Redshift](stored-procedure-create.md)
+ [Referência da linguagem PL/pgSQL](c_pl_pgSQL_reference.md)

# Visão geral dos procedimentos armazenados no Amazon Redshift
<a name="stored-procedure-create"></a>

Este tópico apresenta detalhes sobre a finalidade e o uso dos procedimentos armazenados.

Procedimentos armazenados são comumente usados para encapsular lógica de transformação e validação de dados e lógica empresarial específica. A combinação de várias etapas de SQL em um procedimento armazenado permite reduzir a comunicação entre os aplicativos e o banco de dados.

Para obter controle de acesso granular, você pode criar procedimentos armazenados para executar funções sem precisar conceder ao usuário acesso às tabelas subjacentes. Por exemplo, somente o proprietário ou um superusuário pode truncar uma tabela, e um usuário precisa de privilégios de gravação para inserir dados em uma tabela. Em vez de conceder privilégios a um usuário nas tabelas subjacentes, é possível criar um procedimento armazenado que realiza a tarefa. Depois, conceda ao usuário privilégios para executar o procedimento armazenado. 

Um procedimento armazenado com o atributo de segurança DEFINER é executado com os privilégios do proprietário do procedimento armazenado. Por padrão, um procedimento armazenado tem segurança INVOKER, o que significa que o procedimento usa os privilégios do usuário que chama o procedimento. 

Para criar um procedimento armazenado, use o comando [CREATE PROCEDURE](r_CREATE_PROCEDURE.md). Para executar um procedimento, use o comando [CALL](r_CALL_procedure.md). Exemplos são apresentados posteriormente nessa seção.

**nota**  
Alguns clientes podem exibir o seguinte erro ao criar um procedimento armazenado do Amazon Redshift.  

```
ERROR: 42601: [Amazon](500310) unterminated dollar-quoted string at or near "$$
```
Esse erro ocorre devido à impossibilidade do cliente analisar corretamente a instrução CREATE PROCEDURE com instruções delimitadas por ponto e vírgula e com o cifrão (\$1) entre aspas. Isso resulta em apenas uma parte do extrato enviado ao servidor Amazon Redshift. Geralmente esse erro pode ser resolvido usando a opção `Run as batch` ou `Execute selected` do cliente.   
Por exemplo, ao usar um cliente Aginity, use a opção `Run entire script as batch`. Ao usar SQL Workbench/J, recomendamos a versão 124. Ao usar SQL Workbench/J versão 125, considere a possibilidade de especificar um delimitador alternativo como solução temporária.   
CREATE PROCEDURE contém instruções SQL delimitadas por ponto e vírgula (;). Definir um delimitador alternativo, como uma barra (/) e colocá-lo no final da instrução CREATE PROCEDURE envia a instrução inteira ao servidor do Amazon Redshift para processamento. Veja um exemplo a seguir.  

```
CREATE OR REPLACE PROCEDURE test()
AS $$
BEGIN
  SELECT 1 a;
END;
$$
LANGUAGE plpgsql
;
/
```
É possível usar um cliente com suporte para analisar instruções CREATE PROCEDURE, como o [editor de consultas no console do Amazon Redshift](https://docs.aws.amazon.com/redshift/latest/mgmt/query-editor.html) ou o TablePlus. 

**Topics**
+ [Nomeação de procedimentos armazenados](stored-procedure-naming.md)
+ [Segurança e privilégios para procedimentos armazenados](stored-procedure-security-and-privileges.md)
+ [Exibir um conjunto de resultados de um procedimento armazenado](stored-procedure-result-set.md)
+ [Gerenciamento de transações](stored-procedure-transaction-management.md)
+ [Retenção de erros](stored-procedure-trapping-errors.md)
+ [Registro em log de procedimentos armazenados](c_PLpgSQL-logging.md)
+ [Limitações dos procedimentos armazenados](stored-procedure-constraints.md)

O exemplo a seguir mostra um procedimento sem argumentos de saída. Por padrão, os argumentos são de entrada (IN).

```
CREATE OR REPLACE PROCEDURE test_sp1(f1 int, f2 varchar)
AS $$
BEGIN
  RAISE INFO 'f1 = %, f2 = %', f1, f2;
END;
$$ LANGUAGE plpgsql;

call test_sp1(5, 'abc');
INFO: f1 = 5, f2 = abc
CALL
```



**nota**  
 Quando você escreve procedimentos armazenados, recomendamos a prática de proteger valores confidenciais:   
Não codifique nenhuma informação confidencial na lógica do procedimento armazenado. Por exemplo, não atribua uma senha de usuário em uma instrução CREATE USER no corpo de um procedimento armazenado. Isso representa um risco de segurança, pois valores codificados podem ser registrados como metadados de esquema nas tabelas de catálogos. Em vez disso, transmita valores confidenciais, como senhas, como argumentos ao procedimento armazenado por meio de parâmetros.   
Para obter mais informações sobre os procedimentos armazenados, consulte [CREATE PROCEDURE](r_CREATE_PROCEDURE.md) e [Criar procedimentos armazenados no Amazon Redshift](stored-procedure-overview.md). Para obter mais informações sobre tabelas de catálogos, consulte [Tabelas de catálogo do sistema](c_intro_catalog_views.md).

O exemplo a seguir mostra um procedimento com argumentos de saída. Os argumentos são de entrada (IN), entrada e saída (INOUT) e saída (OUT).

```
CREATE OR REPLACE PROCEDURE test_sp2(f1 IN int, f2 INOUT varchar(256), out_var OUT varchar(256))
AS $$
DECLARE
  loop_var int;
BEGIN
  IF f1 is null OR f2 is null THEN
    RAISE EXCEPTION 'input cannot be null';
  END IF;
  DROP TABLE if exists my_etl;
  CREATE TEMP TABLE my_etl(a int, b varchar);
    FOR loop_var IN 1..f1 LOOP
        insert into my_etl values (loop_var, f2);
        f2 := f2 || '+' || f2;
    END LOOP;
  SELECT INTO out_var count(*) from my_etl;
END;
$$ LANGUAGE plpgsql;


call test_sp2(2,'2019');

         f2          | column2
---------------------+---------
 2019+2019+2019+2019 | 2
(1 row)
```

# Nomeação de procedimentos armazenados
<a name="stored-procedure-naming"></a>

Este tópico apresenta detalhes sobre nomes de procedimentos armazenados.

Se você definir um procedimento com o mesmo nome e diferentes assinaturas ou tipos de dados de argumento de entrada, um novo procedimento será criado. Como resultado, o nome do procedimento será sobrecarregado. Para obter mais informações, consulte [Sobrecarga de nomes de procedimento](#stored-procedure-overloading-name). O Amazon Redshift não habilita a sobrecarga de procedimentos com base em argumentos de saída. Não é possível ter dois procedimentos com o mesmo nome e tipos de dados de argumento de entrada, mas diferentes tipos de argumento de saída.

O proprietário ou um usuário avançado pode substituir o corpo de um procedimento armazenado por um novo com a mesma assinatura. Para alterar a assinatura ou os tipos de retorno de um procedimento armazenado, descarte o procedimento armazenado e crie-o novamente. Para obter mais informações, consulte [DROP PROCEDURE](r_DROP_PROCEDURE.md) e [CREATE PROCEDURE](r_CREATE_PROCEDURE.md).

Você pode evitar potenciais conflitos e resultados inesperados considerando suas convenções de nomenclatura para procedimentos armazenados antes de implementá-los. Como você pode sobrecarregar os nomes de procedimentos, eles podem entrar em conflito com nomes de procedimentos existentes e futuros do Amazon Redshift.

## Sobrecarga de nomes de procedimento
<a name="stored-procedure-overloading-name"></a>

Um procedimento é identificado por seu nome e assinatura, que é o número de argumentos de entrada e tipos de dados dos argumentos. Dois procedimentos no mesmo esquema podem ter o mesmo nome se tiverem assinaturas diferentes. Em outras palavras, é possível sobrecarregar nomes de procedimento.

Ao executar um procedimento, o mecanismo de consulta determina qual procedimento chamar com base no número de argumentos fornecidos e nos tipos de dados dos argumentos. Você pode usar o sobrecarregamento para simular procedimentos com um número variável de argumentos até o limite permitido pelo comando CREATE PROCEDURE. Para obter mais informações, consulte [CREATE PROCEDURE](r_CREATE_PROCEDURE.md).

## Evitar conflitos de nomenclatura
<a name="stored-procedure-name-conflicts"></a>

Recomendamos nomear todos os procedimentos usando o prefixo `sp_`. O Amazon Redshift reserva o prefixo `sp_` para procedimentos armazenados. Ao prefixar seus nomes de procedimento com `sp_`, você garante que seu nome de procedimento não entrará em conflito com nenhum nome de procedimento existente ou futuro do Amazon Redshift. 

# Segurança e privilégios para procedimentos armazenados
<a name="stored-procedure-security-and-privileges"></a>

Este tópico descreve as credenciais de banco de dados necessárias para criar e executar procedimentos armazenados.

Por padrão, todos os usuários têm privilégios para criar um procedimento. Para criar um procedimento, é necessário ter o privilégio USAGE na linguagem PL/pgSQL, que é concedida para PUBLIC, por padrão. Por padrão, somente superusuários e proprietários têm privilégio para chamar um procedimento. Usuários avançados podem executar REVOKE USAGE em PL/pgSQL de um usuário se quiserem impedir que o usuário crie um procedimento armazenado. 

Para chamar um procedimento, é necessário receber privilégio para realizar EXECUTE no procedimento. Por padrão, o privilégio para realizar EXECUTE em novos procedimentos é concedido ao proprietário do procedimento e a superusuários. Para obter mais informações, consulte [GRANT](r_GRANT.md). 

O usuário que cria um procedimento é o proprietário, por padrão. O proprietário possui os privilégios CREATE, DROP e EXECUTE sobre o procedimento, por padrão. Os usuários avançados têm todos os privilégios. 

O atributo SECURITY controla os privilégios de um procedimento para acessar objetos do banco de dados. Ao criar um procedimento armazenado, é possível definir o atributo SECURITY como DEFINER ou INVOKER. Esse atributo determina quais privilégios são usados ao executar as instruções no corpo do procedimento armazenado. Se você especificar SECURITY INVOKER, o procedimento usará os privilégios do usuário que invoca o procedimento. Se você especificar SECURITY DEFINER, o procedimento usará os privilégios do proprietário do procedimento. INVOKER é o padrão. 

Como um procedimento SECURITY DEFINER é executado com os privilégios do usuário que é proprietário dele, você deve garantir que o procedimento não seja mal utilizado. Para garantir que procedimentos SECURITY DEFINER não sejam mal utilizados, faça o seguinte:
+ Conceda EXECUTE em procedimentos SECURITY DEFINER para usuários específicos em vez de PUBLIC.
+ Qualifique todos os objetos do banco de dados que o procedimento precisa acessar com os nomes de esquema. Por exemplo, use `myschema.mytable`, em vez de apenas `mytable`.
+ Se não for possível qualificar um nome de objeto pelo seu esquema, defina `search_path` ao criar o procedimento usando a opção SET. Defina `search_path` para excluir quaisquer esquemas que sejam graváveis por usuários não confiáveis. Essa abordagem impede que qualquer chamador desse procedimento crie objetos (por exemplo, tabelas ou visualizações) que mascaram objetos destinados ao uso pelo procedimento. Para obter mais informações sobre a opção SET, consulte [CREATE PROCEDURE](r_CREATE_PROCEDURE.md). 

O exemplo a seguir define `search_path` como `admin` para garantir que a tabela `user_creds` seja acessada a partir do esquema `admin` e não publicamente ou de qualquer outro esquema no `search_path` do chamador.

```
CREATE OR REPLACE PROCEDURE sp_get_credentials(userid int, o_creds OUT varchar)
AS $$
BEGIN
  SELECT creds INTO o_creds
  FROM user_creds
  WHERE user_id = $1;
END;
$$ LANGUAGE plpgsql
SECURITY DEFINER
-- Set a secure search_path
SET search_path = admin;
```

# Exibir um conjunto de resultados de um procedimento armazenado
<a name="stored-procedure-result-set"></a>

Este tópico descreve como os procedimentos armazenados exibem dados.

É possível retornar um conjunto de resultados usando um cursor ou uma tabela temporária.

## Retorno de um cursor
<a name="stored-procedure-return-cursor"></a>

Para retornar um cursor, crie um procedimento com um argumento INOUT definido com um tipo de dados `refcursor`. Ao chamar o procedimento, dê um nome ao cursor. Depois, você pode buscar os resultados do cursor pelo nome.

O exemplo a seguir cria um procedimento chamado `get_result_set` com um argumento INOUT chamado `rs_out` usando o tipo de dados `refcursor`. O procedimento abre o cursor usando uma instrução SELECT.

```
CREATE OR REPLACE PROCEDURE get_result_set (param IN integer, rs_out INOUT refcursor)
AS $$
BEGIN
  OPEN rs_out FOR SELECT * FROM fact_tbl where id >= param;
END;
$$ LANGUAGE plpgsql;
```

O comando CALL a seguir abre o cursor com o nome `mycursor`. Use cursores somente nas transações. 

```
BEGIN;
CALL get_result_set(1, 'mycursor');
```

Depois que o cursor for aberto, você pode buscar a partir do cursor, conforme mostram os exemplos a seguir.

```
FETCH ALL FROM mycursor;

    id | secondary_id | name
-------+--------------+---------
     1 |            1 | Joe
     1 |            2 | Ed
     2 |            1 | Mary
     1 |            3 | Mike
(4 rows)
```

Ao final, a transação é confirmada ou revertida.

```
COMMIT;   
```

Um cursor retornado por um procedimento armazenado está sujeito às mesmas restrições e considerações de performance, conforme descrito em DECLARE CURSOR. Para obter mais informações, consulte [Restrições de cursor](declare.md#declare-constraints).

O exemplo a seguir mostra a chamada do procedimento armazenado `get_result_set` usando um tipo de dados `refcursor` a partir do JDBC. O `'mycursor'` literal (o nome do cursor) é enviado para a `prepareStatement`. Depois, os resultados são obtidos do `ResultSet`.

```
static void refcursor_example(Connection conn) throws SQLException {
    conn.setAutoCommit(false);
    PreparedStatement proc = conn.prepareStatement("CALL get_result_set(1, 'mycursor')");
    proc.execute();
    ResultSet rs = statement.executeQuery("fetch all from mycursor");
    while (rs.next()) {
      int n = rs.getInt(1);
      System.out.println("n " + n);
    }
```

## Uso de uma tabela temporária
<a name="stored-procedure-return-cursor"></a>

Para retornar resultados, você pode retornar um identificador para uma tabela temporária que contém linhas de resultados. O cliente pode fornecer um nome como um parâmetro para o procedimento armazenado. Dentro do procedimento armazenado, SQL dinâmico pode ser usado para operar na tabela temporária. Por exemplo:

```
CREATE PROCEDURE get_result_set(param IN integer, tmp_name INOUT varchar(256)) as $$
DECLARE
  row record;
BEGIN
  EXECUTE 'drop table if exists ' || tmp_name;
  EXECUTE 'create temp table ' || tmp_name || ' as select * from fact_tbl where id <= ' || param;
END;
$$ LANGUAGE plpgsql;

CALL get_result_set(2, 'myresult');
 tmp_name
-----------
 myresult
(1 row)

SELECT * from myresult;
 id | secondary_id | name
----+--------------+------
  1 |            1 | Joe
  2 |            1 | Mary
  1 |            2 | Ed
  1 |            3 | Mike
(4 rows)
```

# Gerenciamento de transações
<a name="stored-procedure-transaction-management"></a>

Você pode criar um procedimento armazenado com comportamento padrão de gerenciamento de transações ou comportamento não atômico. 

## Gerenciamento de transações de procedimentos armazenados no modo padrão
<a name="stored-procedure-transaction-management-default-mode"></a>

O comportamento de confirmação automática do modo de transações padrão faz com que cada comando SQL executado separadamente seja confirmado individualmente. Uma chamada para um procedimento armazenado é tratada como um único comando SQL. As instruções SQL dentro de um procedimento se comportam como se estivessem em um bloco de transações iniciado implicitamente quando a chamada começa, e que termina quando a chamada é encerrada. Uma chamada aninhada para outro procedimento é tratada como qualquer outra instrução SQL e opera dentro do contexto da mesma transação que o chamador. Para obter mais informações sobre o comportamento de confirmação automática, consulte [Níveis de isolamento no Amazon Redshift](c_serial_isolation.md).

Porém, suponha que você chame um procedimento armazenado a partir de um bloco de transações especificado pelo usuário (definido por BEGIN... COMMIT). Neste caso, todas as instruções do procedimento armazenado são executadas no contexto da transação especificada pelo usuário. O procedimento não é confirmado implicitamente na saída. O chamador controla a confirmação ou a reversão do procedimento.

Se um erro for encontrado durante a execução de um procedimento armazenado, todas as alterações feitas na transação atual são revertidas.

Você pode usar as seguintes instruções de controle de transação em um procedimento armazenado:
+ COMMIT - Confirma todo o trabalho realizado na transação atual e inicia implicitamente uma nova transação. Para obter mais informações, consulte [COMMIT](r_COMMIT.md). 
+ ROLLBACK - Reverte o trabalho realizado na transação atual e inicia implicitamente uma nova transação. Para obter mais informações, consulte [ROLLBACK](r_ROLLBACK.md). 

TRUNCATE é outra instrução que pode ser emitida em um procedimento armazenado e influencia o gerenciamento de transações. No Amazon Redshift, TRUNCATE emite uma confirmação implicitamente. Esse comportamento permanece o mesmo no contexto dos procedimentos armazenados. Quando uma instrução TRUNCATE for emitida a partir de um procedimento armazenado, ela confirma a transação atual e inicia uma nova. Para obter mais informações, consulte [TRUNCATE](r_TRUNCATE.md). 

Todas as instruções que seguem uma instrução COMMIT, ROLLBACK ou TRUNCATE no contexto de uma nova transação. Elas fazem isso até uma instrução COMMIT, ROLLBACK ou TRUNCATE ser encontrada ou o procedimento armazenado ser encerrado.

Ao usar uma instrução COMMIT, ROLLBACK ou TRUNCATE em um procedimento armazenado, as seguintes restrições são aplicadas:
+ Se o procedimento armazenado for chamado em um bloco de transação, ele não poderá emitir uma instrução COMMIT, ROLLBACK ou TRUNCATE. Essa restrição se aplica ao corpo do próprio procedimento armazenado e a qualquer chamada de procedimento aninhada.
+ Se o procedimento armazenado for criado com opções `SET config`, ele não poderá emitir uma instrução COMMIT, ROLLBACK ou TRUNCATE. Essa restrição se aplica ao corpo do próprio procedimento armazenado e a qualquer chamada de procedimento aninhada. 
+ Qualquer cursor aberto (explícita ou implicitamente) será fechado automaticamente quando uma instrução COMMIT, ROLLBACK ou TRUNCATE for processada. Para obter as restrições sobre cursores explícitos ou implícitos, consulte [Limitações dos procedimentos armazenados](stored-procedure-constraints.md).

Além disso, não é possível executar COMMIT ou ROLLBACK com SQL dinâmico. No entanto, é possível executar TRUNCATE com SQL dinâmico. Para obter mais informações, consulte [SQL dinâmico](c_PLpgSQL-statements.md#r_PLpgSQL-dynamic-sql). 

Ao trabalhar com procedimentos armazenados, considere que as instruções BEGIN e END em PL/pgSQL são somente para agrupamento. Elas não iniciam ou encerram uma transação. Para obter mais informações, consulte [Bloquear](c_PLpgSQL-structure.md#r_PLpgSQL-block). 

O exemplo a seguir demonstra o comportamento da transação ao chamar um procedimento armazenado a partir de um bloco de transações explícito. As duas instruções de inserção emitidas fora do procedimento armazenado e aquela emitida dentro dele fazem parte da mesma transação (3382). A transação é confirmada quando o usuário emite a confirmação explícita.

```
CREATE OR REPLACE PROCEDURE sp_insert_table_a(a int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO test_table_a values (a);
END;
$$;

Begin;
  insert into test_table_a values (1);
  Call sp_insert_table_a(2);
  insert into test_table_a values (3);
Commit; 

select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  | pid |  type   |               stmt_text
--------+------+-----+---------+----------------------------------------
    103 | 3382 | 599 | UTILITY | Begin;
    103 | 3382 | 599 | QUERY   | insert into test_table_a values (1);
    103 | 3382 | 599 | UTILITY | Call sp_insert_table_a(2);
    103 | 3382 | 599 | QUERY   | INSERT INTO test_table_a values ( $1 )
    103 | 3382 | 599 | QUERY   | insert into test_table_a values (3);
    103 | 3382 | 599 | UTILITY | COMMIT
```

Por outro lado, pense em quando as mesmas instruções forem emitidas fora de um bloco de transações explícito e a sessão tiver a confirmação automática definida como ON. Nesse caso, cada instrução é executada em sua própria transação.

```
insert into test_table_a values (1);
Call sp_insert_table_a(2);
insert into test_table_a values (3);

select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  | pid |  type   |                                                                    stmt_text
--------+------+-----+---------+-------------------------------------------------------------------------------------------------------------------------------------------------
    103 | 3388 | 599 | QUERY   | insert into test_table_a values (1);
    103 | 3388 | 599 | UTILITY | COMMIT
    103 | 3389 | 599 | UTILITY | Call sp_insert_table_a(2);
    103 | 3389 | 599 | QUERY   | INSERT INTO test_table_a values ( $1 )
    103 | 3389 | 599 | UTILITY | COMMIT
    103 | 3390 | 599 | QUERY   | insert into test_table_a values (3);
    103 | 3390 | 599 | UTILITY | COMMIT
```

O exemplo a seguir emite uma instrução TRUNCATE depois de inserir na `test_table_a`. A instrução TRUNCATE emite uma confirmação implícita que confirma a transação atual (3335) e inicia uma nova (3336). A nova transação é confirmada quando o procedimento é encerrado.

```
CREATE OR REPLACE PROCEDURE sp_truncate_proc(a int, b int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO test_table_a values (a);
  TRUNCATE test_table_b;
  INSERT INTO test_table_b values (b);
END;
$$;

Call sp_truncate_proc(1,2);

select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |  pid  |  type   |                                                                                             stmt_text
--------+------+-------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    103 | 3335 | 23636 | UTILITY | Call sp_truncate_proc(1,2);
    103 | 3335 | 23636 | QUERY   | INSERT INTO test_table_a values ( $1 )
    103 | 3335 | 23636 | UTILITY | TRUNCATE test_table_b
    103 | 3335 | 23636 | UTILITY | COMMIT
    103 | 3336 | 23636 | QUERY   | INSERT INTO test_table_b values ( $1 )
    103 | 3336 | 23636 | UTILITY | COMMIT
```

O exemplo a seguir emite uma TRUNCATE a partir de uma chamada aninhada. A TRUNCATE confirma todo o trabalho feito até agora nos procedimentos externo e interno em uma transação (3344). Ela inicia uma nova transação (3345). A nova transação é confirmada quando o procedimento externo é encerrado.

```
CREATE OR REPLACE PROCEDURE sp_inner(c int, d int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO inner_table values (c);
  TRUNCATE outer_table;
  INSERT INTO inner_table values (d);
END;
$$;

CREATE OR REPLACE PROCEDURE sp_outer(a int, b int, c int, d int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO outer_table values (a);
  Call sp_inner(c, d);
  INSERT INTO outer_table values (b);
END;
$$;

Call sp_outer(1, 2, 3, 4);

select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |  pid  |  type   |                                                                                              stmt_text
--------+------+-------+---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    103 | 3344 | 23636 | UTILITY | Call sp_outer(1, 2, 3, 4);
    103 | 3344 | 23636 | QUERY   | INSERT INTO outer_table values ( $1 )
    103 | 3344 | 23636 | UTILITY | CALL sp_inner( $1 , $2 )
    103 | 3344 | 23636 | QUERY   | INSERT INTO inner_table values ( $1 )
    103 | 3344 | 23636 | UTILITY | TRUNCATE outer_table
    103 | 3344 | 23636 | UTILITY | COMMIT
    103 | 3345 | 23636 | QUERY   | INSERT INTO inner_table values ( $1 )
    103 | 3345 | 23636 | QUERY   | INSERT INTO outer_table values ( $1 )
    103 | 3345 | 23636 | UTILITY | COMMIT
```

O exemplo a seguir mostra que o cursor `cur1` foi fechado quando a instrução TRUNCATE foi confirmada.

```
CREATE OR REPLACE PROCEDURE sp_open_cursor_truncate()
LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
  cur1 cursor for select * from test_table_a order by 1;
BEGIN
  open cur1;
  TRUNCATE table test_table_b;
  Loop
    fetch cur1 into rec;
    raise info '%', rec.c1;
    exit when not found;
  End Loop;
END
$$;

call sp_open_cursor_truncate();
ERROR: cursor "cur1" does not exist
CONTEXT: PL/pgSQL function "sp_open_cursor_truncate" line 8 at fetch
```

O exemplo a seguir emite uma instrução TRUNCATE e não pode ser chamado dentro de um bloco de transações explícito.

```
CREATE OR REPLACE PROCEDURE sp_truncate_atomic() LANGUAGE plpgsql
AS $$
BEGIN
  TRUNCATE test_table_b;
END;
$$;

Begin;
  Call sp_truncate_atomic();
ERROR: TRUNCATE cannot be invoked from a procedure that is executing in an atomic context.
HINT: Try calling the procedure as a top-level call i.e. not from within an explicit transaction block. 
Or, if this procedure (or one of its ancestors in the call chain) was created with SET config options, recreate the procedure without them.
CONTEXT: SQL statement "TRUNCATE test_table_b"
PL/pgSQL function "sp_truncate_atomic" line 2 at SQL statement
```

O exemplo a seguir mostra que um usuário que não é um superusuário ou proprietário de uma tabela pode emitir uma instrução TRUNCATE na tabela. O usuário faz isso usando um procedimento `Security Definer` armazenado. O exemplo mostra as seguintes ações: 
+ O user1 cria a tabela `test_tbl`. 
+ O user1 cria o procedimento armazenado `sp_truncate_test_tbl`. 
+ O user1 concede privilégio `EXECUTE` no procedimento armazenado para user2. 
+ O user2 executa o procedimento armazenado para truncar a tabela `test_tbl`. O exemplo mostra a contagem de linhas antes e depois do comando `TRUNCATE`. 

```
set session_authorization to user1;
create table test_tbl(id int, name varchar(20));
insert into test_tbl values (1,'john'), (2, 'mary');
CREATE OR REPLACE PROCEDURE sp_truncate_test_tbl() LANGUAGE plpgsql
AS $$
DECLARE
  tbl_rows int;
BEGIN
  select count(*) into tbl_rows from test_tbl;
  RAISE INFO 'RowCount before Truncate: %', tbl_rows;
  TRUNCATE test_tbl;
  select count(*) into tbl_rows from test_tbl;
  RAISE INFO 'RowCount after Truncate: %', tbl_rows;
END;
$$ SECURITY DEFINER;
grant execute on procedure sp_truncate_test_tbl() to user2;
reset session_authorization;


set session_authorization to user2;
call sp_truncate_test_tbl();
INFO:  RowCount before Truncate: 2
INFO:  RowCount after Truncate: 0
CALL
reset session_authorization;
```

O exemplo a seguir emite COMMIT duas vezes. O primeiro COMMIT confirma todo o trabalho feito na transação 10363 e começa implicitamente a transação 10364. A transação 10364 será confirmada pela segunda instrução COMMIT. 

```
CREATE OR REPLACE PROCEDURE sp_commit(a int, b int) LANGUAGE plpgsql
AS $$
BEGIN
  INSERT INTO test_table values (a);
  COMMIT;
  INSERT INTO test_table values (b);
  COMMIT;
END;
$$;

call sp_commit(1,2);

select userid, xid, pid, type, trim(text) as stmt_text
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;
 userid |  xid  | pid  |  type   |                                                                                    stmt_text
--------+-------+------+---------+-----------------------------------------------------------------------------------------------------------------
    100 | 10363 | 3089 | UTILITY | call sp_commit(1,2);
    100 | 10363 | 3089 | QUERY   | INSERT INTO test_table values ( $1 )
    100 | 10363 | 3089 | UTILITY | COMMIT
    100 | 10364 | 3089 | QUERY   | INSERT INTO test_table values ( $1 )
    100 | 10364 | 3089 | UTILITY | COMMIT
```

O exemplo a seguir emitirá uma instrução ROLLBACK se `sum_vals` for maior do que 2. A primeira instrução ROLLBACK reverte todo o trabalho feito na transação 10377 e começa uma nova transação 10378. A transação 10378 é confirmada quando o procedimento é encerrado. 

```
CREATE OR REPLACE PROCEDURE sp_rollback(a int, b int) LANGUAGE plpgsql
AS $$
DECLARE
  sum_vals int;
BEGIN
  INSERT INTO test_table values (a);
  SELECT sum(c1) into sum_vals from test_table;
  IF sum_vals > 2 THEN
    ROLLBACK;
  END IF;
  
  INSERT INTO test_table values (b);
END;
$$;

call sp_rollback(1, 2);

select userid, xid, pid, type, trim(text) as stmt_text
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

userid |  xid  | pid  |  type   |                                                                                    stmt_text
--------+-------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    100 | 10377 | 3089 | UTILITY | call sp_rollback(1, 2);
    100 | 10377 | 3089 | QUERY   | INSERT INTO test_table values ( $1 )
    100 | 10377 | 3089 | QUERY   | SELECT sum(c1) from test_table
    100 | 10377 | 3089 | QUERY   | Undoing 1 transactions on table 133646 with current xid 10377 : 10377
    100 | 10378 | 3089 | QUERY   | INSERT INTO test_table values ( $1 )
    100 | 10378 | 3089 | UTILITY | COMMIT
```

## Gerenciamento de transações de procedimentos armazenados no modo não atômico
<a name="stored-procedure-transaction-management-nonatomic-mode"></a>

Um procedimento armazenado criado no modo NONATOMIC tem um comportamento de controle de transações diferente de um procedimento criado no modo padrão. Semelhante ao comportamento de confirmação automática dos comandos SQL fora dos procedimentos armazenados, cada instrução SQL dentro de um procedimento NONATOMIC é executada em sua própria transação e é confirmada automaticamente. Se um usuário iniciar um bloco de transação explícito em um procedimento armazenado NONATOMIC, as instruções SQL dentro do bloco não serão confirmadas automaticamente. O bloco de transação controla a confirmação ou a reversão das instruções contidas nele. 

Em procedimentos armazenados NONATOMIC, você pode abrir um bloco de transação explícito dentro do procedimento usando a instrução START TRANSACTION. No entanto, se já houver um bloco de transação aberto, essa instrução não fará nada porque o Amazon Redshift não é compatível com subtransações. A transação anterior continua.

Ao trabalhar com loops FOR do cursor dentro de um procedimento NONATOMIC, certifique-se de abrir um bloco de transação explícito antes de percorrer os resultados de uma consulta. Caso contrário, o cursor será fechado quando a instrução SQL dentro do loop for confirmada automaticamente.

Algumas considerações ao usar o comportamento do modo NONATOMIC são as seguintes:
+ Cada instrução SQL dentro do procedimento armazenado será confirmada automaticamente se não houver um bloco de transação aberto e se a confirmação automática estiver ativada na sessão.
+ Você pode emitir uma instrução COMMIT/ROLLBACK/TRUNCATE para encerrar a transação se o procedimento armazenado for chamado de dentro de um bloco de transação. Isso não é possível no modo padrão.
+ Você pode emitir uma instrução START TRANSACTION para iniciar um bloco de transação dentro do procedimento armazenado.

Os exemplos a seguir demonstram o comportamento da transação ao trabalhar com procedimentos armazenados NONATOMIC. A sessão para todos os exemplos a seguir está com a confirmação automática ativada.

No exemplo a seguir, um procedimento armazenado NONATOMIC tem duas instruções INSERT. Quando o procedimento é chamado fora de um bloco de transação, cada instrução INSERT dentro do procedimento é confirmada automaticamente. 

```
CREATE TABLE test_table_a(v int); 
CREATE TABLE test_table_b(v int); 

CREATE OR REPLACE PROCEDURE sp_nonatomic_insert_table_a(a int, b int) NONATOMIC AS
$$
BEGIN
    INSERT INTO test_table_a values (a);
    INSERT INTO test_table_b values (b);
END;
$$ 
LANGUAGE plpgsql;

Call sp_nonatomic_insert_table_a(1,2);

Select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |    pid     |  type   |               stmt_text                
--------+------+------------+---------+----------------------------------------
      1 | 1792 | 1073807554 | UTILITY | Call sp_nonatomic_insert_table_a(1,2);
      1 | 1792 | 1073807554 | QUERY   | INSERT INTO test_table_a values ( $1 )
      1 | 1792 | 1073807554 | UTILITY | COMMIT
      1 | 1793 | 1073807554 | QUERY   | INSERT INTO test_table_b values ( $1 )
      1 | 1793 | 1073807554 | UTILITY | COMMIT
(5 rows)
```

No entanto, quando o procedimento é chamado de dentro de um bloco BEGIN…COMMIT, todas as instruções fazem parte da mesma transação (xid=1799). 

```
Begin;
  INSERT INTO test_table_a values (10);
  Call sp_nonatomic_insert_table_a(20,30);
  INSERT INTO test_table_b values (40);
Commit; 

Select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |    pid     |  type   |                stmt_text                 
--------+------+------------+---------+------------------------------------------
      1 | 1799 | 1073914035 | UTILITY | Begin;
      1 | 1799 | 1073914035 | QUERY   | INSERT INTO test_table_a values (10);
      1 | 1799 | 1073914035 | UTILITY | Call sp_nonatomic_insert_table_a(20,30);
      1 | 1799 | 1073914035 | QUERY   | INSERT INTO test_table_a values ( $1 )
      1 | 1799 | 1073914035 | QUERY   | INSERT INTO test_table_b values ( $1 )
      1 | 1799 | 1073914035 | QUERY   | INSERT INTO test_table_b values (40);
      1 | 1799 | 1073914035 | UTILITY | COMMIT
(7 rows)
```

Neste exemplo, duas instruções INSERT estão entre START TRANSACTION...COMMIT. Quando o procedimento é chamado fora de um bloco de transação, as duas instruções INSERT estão na mesma transação (xid=1866). 

```
CREATE OR REPLACE PROCEDURE sp_nonatomic_txn_block(a int, b int) NONATOMIC AS
$$
BEGIN
    START TRANSACTION;
    INSERT INTO test_table_a values (a);
    INSERT INTO test_table_b values (b);
    COMMIT;
END;
$$ 
LANGUAGE plpgsql;

Call sp_nonatomic_txn_block(1,2);

Select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |    pid     |  type   |               stmt_text                
--------+------+------------+---------+----------------------------------------
      1 | 1865 | 1073823998 | UTILITY | Call sp_nonatomic_txn_block(1,2);
      1 | 1866 | 1073823998 | QUERY   | INSERT INTO test_table_a values ( $1 )
      1 | 1866 | 1073823998 | QUERY   | INSERT INTO test_table_b values ( $1 )
      1 | 1866 | 1073823998 | UTILITY | COMMIT
(4 rows)
```

Quando o procedimento é chamado de dentro de um bloco BEGIN...COMMIT, o comando START TRANSACTION dentro do procedimento não faz nada porque já existe uma transação aberta. O comando COMMIT dentro do procedimento confirma a transação atual (xid=1876) e inicia uma nova.

```
Begin;
  INSERT INTO test_table_a values (10);
  Call sp_nonatomic_txn_block(20,30);
  INSERT INTO test_table_b values (40);
Commit; 

Select userid, xid, pid, type, trim(text) as stmt_text 
from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence;

 userid | xid  |    pid     |  type   |               stmt_text                
--------+------+------------+---------+----------------------------------------
      1 | 1876 | 1073832133 | UTILITY | Begin;
      1 | 1876 | 1073832133 | QUERY   | INSERT INTO test_table_a values (10);
      1 | 1876 | 1073832133 | UTILITY | Call sp_nonatomic_txn_block(20,30);
      1 | 1876 | 1073832133 | QUERY   | INSERT INTO test_table_a values ( $1 )
      1 | 1876 | 1073832133 | QUERY   | INSERT INTO test_table_b values ( $1 )
      1 | 1876 | 1073832133 | UTILITY | COMMIT
      1 | 1878 | 1073832133 | QUERY   | INSERT INTO test_table_b values (40);
      1 | 1878 | 1073832133 | UTILITY | COMMIT
(8 rows)
```

Este exemplo mostra como trabalhar com loops de cursor. A tabela test\$1table\$1a tem três valores. O objetivo é percorrer os três valores e inseri-los na tabela test\$1table\$1b. Se um procedimento armazenado NONATOMIC for criado conforme mostrado a seguir, será gerado um erro informando que o cursor “cur1" não existe depois da execução da instrução INSERT no primeiro loop. Isso ocorre porque a confirmação automática de INSERT fecha o cursor aberto.

```
insert into test_table_a values (1), (2), (3);

CREATE OR REPLACE PROCEDURE sp_nonatomic_cursor() NONATOMIC
LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
  cur1 cursor for select * from test_table_a order by 1;
BEGIN
  open cur1;
  Loop
    fetch cur1 into rec;
    exit when not found;
    raise info '%', rec.v;
    insert into test_table_b values (rec.v);
  End Loop;
END
$$;

CALL sp_nonatomic_cursor();

INFO:  1
ERROR:  cursor "cur1" does not exist
CONTEXT:  PL/pgSQL function "sp_nonatomic_cursor" line 7 at fetch
```

Para que o loop do cursor funcione, coloque-o entre START TRANSACTION...COMMIT.

```
insert into test_table_a values (1), (2), (3);

CREATE OR REPLACE PROCEDURE sp_nonatomic_cursor() NONATOMIC
LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
  cur1 cursor for select * from test_table_a order by 1;
BEGIN
  START TRANSACTION;
  open cur1;
  Loop
    fetch cur1 into rec;
    exit when not found;
    raise info '%', rec.v;
    insert into test_table_b values (rec.v);
  End Loop;
  COMMIT;
END
$$;

CALL sp_nonatomic_cursor();

INFO:  1
INFO:  2
INFO:  3
CALL
```

# Retenção de erros
<a name="stored-procedure-trapping-errors"></a>

Este tópico descreve como o Amazon Redshift lida com erros.

Quando uma consulta ou comando em um procedimento armazenado gera um erro, as consultas subsequentes não são executadas e a transação é revertida. Mas você pode tratar os erros usando um bloco EXCEPTION.

**nota**  
O comportamento padrão é que um erro fará com que as consultas subsequentes não sejam executadas, mesmo quando não há outras condições de geração de erros no procedimento armazenado.

```
[ <<label>> ]
[ DECLARE
  declarations ]
BEGIN
  statements
EXCEPTION
  WHEN OTHERS THEN
    statements
END;
```

Quando ocorre uma exceção e você adiciona um bloco de tratamento de exceções, é possível gravar instruções RAISE e a maioria das outras instruções PL/PGSQL. Por exemplo, é possível levantar uma exceção com uma mensagem personalizada ou inserir um registro em uma tabela de registro.

Ao inserir o bloco de tratamento de exceções, a transação atual é revertida, e cria-se uma nova transação para executar as instruções no bloco. Se as instruções no bloco forem executadas sem erros, a transação será confirmada, e a exceção será lançada novamente. Por fim, o procedimento armazenado é encerrado.

A única condição compatível em um bloco de exceção é OTHERS, que corresponde a todos os tipos de erros, exceto o cancelamento de consulta. Além disso, se ocorrer um erro em um bloco de tratamento de exceções, ele poderá ser detectado por um bloco de tratamento de exceções externo.

Quando ocorre um erro dentro do procedimento NONATOMIC, o erro não será relançado se for tratado por um bloco de exceções. Consulte a instrução PL/pgSQL `RAISE` para lançar uma exceção capturada pelo bloco de tratamento de exceções. Essa instrução só é válida em blocos de tratamento de exceções. Para obter mais informações, consulte [RAISE](c_PLpgSQL-statements.md#r_PLpgSQL-messages-errors).

**Controle do que acontecerá depois de um erro em um procedimento armazenado, com o manipulador CONTINUE**

 O manipulador `CONTINUE` é um tipo de manipulador de exceções que controla o fluxo de execuções em um procedimento armazenado NONATOMIC. Ao usá-lo, você pode capturar e processar exceções sem encerrar o bloco de instruções existente. Normalmente, quando ocorre um erro em um procedimento armazenado, o fluxo é interrompido, e o erro é retornado ao chamador. No entanto, em alguns casos de uso, a condição de erro não é grave o suficiente para justificar a interrupção do fluxo. Convém processar o erro normalmente, usando a lógica de tratamento de erros de sua preferência em uma transação à parte e, em seguida, continuar executando as instruções que seguem o erro. Esta é a sintaxe. 

```
[ DECLARE
  declarations ]
BEGIN
  statements
EXCEPTION
  [ CONTINUE_HANDLER | EXIT_HANDLER ] WHEN OTHERS THEN
    handler_statements
END;
```

Há várias tabelas do sistema disponíveis para ajudar você a coletar informações sobre vários tipos de erros. Para obter mais informações, consulte [STL\$1LOAD\$1ERRORS](r_STL_LOAD_ERRORS.md), [STL\$1ERROR](r_STL_ERROR.md) e [SYS\$1STREAM\$1SCAN\$1ERRORS](r_SYS_STREAM_SCAN_ERRORS.md). Também há tabelas de sistema adicionais que você pode usar para solucionar erros. Mais informações sobre elas podem ser encontradas em [Referência de visualizações e tabelas do sistema](cm_chap_system-tables.md).

## Exemplo
<a name="stored-procedure-trapping-errors-examples"></a>

O exemplo a seguir mostra como gravar instruções no bloco de tratamento de exceções. O procedimento armazenado está usando o comportamento padrão de gerenciamento de transações.

```
CREATE TABLE employee (firstname varchar, lastname varchar);
INSERT INTO employee VALUES ('Tomas','Smith');
CREATE TABLE employee_error_log (message varchar);

CREATE OR REPLACE PROCEDURE update_employee_sp() AS
$$
BEGIN
    UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
    EXECUTE 'select invalid';
EXCEPTION WHEN OTHERS THEN
    RAISE INFO 'An exception occurred.';
    INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp();

INFO:  An exception occurred.
ERROR:  column "invalid" does not exist
CONTEXT:  SQL statement "select invalid"
PL/pgSQL function "update_employee_sp" line 3 at execute statement
```

Neste exemplo, se chamarmos `update_employee_sp`, é gerada a mensagem informativa *An exception occurred.* (Ocorreu uma exceção), e a mensagem de erro é inserida no log `employee_error_log` da tabela de logs. A exceção original é lançada novamente antes que o procedimento armazenado seja encerrado. As consultas a seguir mostram registros resultantes da execução do exemplo.

```
SELECT * from employee;

firstname | lastname 
-----------+----------
 Tomas     | Smith

SELECT * from employee_error_log;

          message                     
------------------------------------------------
 Error message: column "invalid" does not exist
```

Para obter mais informações sobre RAISE, inclusive ajuda para formatação e uma lista dos níveis adicionais, consulte [Instruções da PL/pgSQL compatíveis](c_PLpgSQL-statements.md).

O exemplo a seguir mostra como gravar instruções no bloco de tratamento de exceções. O procedimento armazenado está usando o comportamento NONATOMIC de gerenciamento de transações. Neste exemplo, nenhum erro é revertido ao chamador depois da conclusão da chamada do procedimento. A instrução UPDATE não é revertida devido ao erro na próxima instrução. A mensagem informativa é gerada e a mensagem de erro é inserida na tabela de log.

```
CREATE TABLE employee (firstname varchar, lastname varchar); 
INSERT INTO employee VALUES ('Tomas','Smith'); 
CREATE TABLE employee_error_log (message varchar);

-- Create the SP in NONATOMIC mode
CREATE OR REPLACE PROCEDURE update_employee_sp_2() NONATOMIC AS
$$
BEGIN
    UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
    EXECUTE 'select invalid';
EXCEPTION WHEN OTHERS THEN
    RAISE INFO 'An exception occurred.';
    INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp_2();
INFO:  An exception occurred.
CALL

SELECT * from employee;

 firstname | lastname 
-----------+----------
 Adam      | Smith
(1 row)

SELECT * from employee_error_log;

                    message                     
------------------------------------------------
 Error message: column "invalid" does not exist
(1 row)
```

Este exemplo mostra como criar um procedimento armazenado em dois sub-blocos. Quando o procedimento armazenado é chamado, o erro do primeiro sub-bloco é tratado por seu bloco de tratamento de exceções. Após a conclusão do primeiro sub-bloco, o procedimento continua executando o segundo sub-bloco. Pelo resultado, podemos ver que nenhum erro é gerado quando a chamada do procedimento é concluída. As operações UPDATE e INSERT na tabela “employee” são confirmadas. As mensagens de erro dos dois blocos de exceção são inseridas na tabela de log.

```
CREATE TABLE employee (firstname varchar, lastname varchar); 
INSERT INTO employee VALUES ('Tomas','Smith'); 
CREATE TABLE employee_error_log (message varchar);

CREATE OR REPLACE PROCEDURE update_employee_sp_3() NONATOMIC AS
$$
BEGIN
    BEGIN
        UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
        EXECUTE 'select invalid1';
    EXCEPTION WHEN OTHERS THEN
        RAISE INFO 'An exception occurred in the first block.';
        INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
    END;
    BEGIN
        INSERT INTO employee VALUES ('Edie','Robertson');
        EXECUTE 'select invalid2';
    EXCEPTION WHEN OTHERS THEN
        RAISE INFO 'An exception occurred in the second block.';
        INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
    END;
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp_3();
INFO:  An exception occurred in the first block.
INFO:  An exception occurred in the second block.
CALL

SELECT * from employee;

 firstname | lastname  
-----------+-----------
 Adam      | Smith
 Edie      | Robertson
(2 rows)

SELECT * from employee_error_log;

                     message                     
-------------------------------------------------
 Error message: column "invalid1" does not exist
 Error message: column "invalid2" does not exist
(2 rows)
```

O exemplo a seguir mostra como usar o manipulador de exceções CONTINUE. Esse exemplo cria duas tabelas e as usa em um procedimento armazenado. O manipulador CONTINUE controla o fluxo de execução em um procedimento armazenado com comportamento de gerenciamento de transações NONATOMIC.

```
CREATE TABLE tbl_1 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_1() NONATOMIC AS
$$
BEGIN
    INSERT INTO tbl_1 VALUES (1);
    -- Expect an error for the insert statement following, because of the invalid value
    INSERT INTO tbl_1 VALUES ("val");
    INSERT INTO tbl_1 VALUES (2);
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

Chame o procedimento armazenado:

```
CALL sp_exc_handling_1();
```

O fluxo avança assim:

1. Ocorre um erro porque é feita uma tentativa de inserir um tipo de dados incompatível em uma coluna. O controle passa para o bloco EXCEPTION. Quando o bloco de tratamento de exceções é acessado, a transação atual é revertida e uma nova transação implícita é criada para executar as instruções nela.

1. Se as instruções em CONTINUE\$1HANDLER forem executadas sem erros, o controle passará imediatamente para a instrução depois da instrução que estiver causando a exceção. (Se uma instrução em CONTINUE\$1HANDLER gerar uma nova exceção, você poderá tratá-la com um manipulador de exceções dentro do bloco EXCEPTION.)

Depois de você chamar o procedimento armazenado de amostra, as tabelas conterão os seguintes registros:
+ Se você executar `SELECT * FROM tbl_1;`, ele retornará dois registros. Eles contêm os valores `1` e `2`.
+ Se você executar `SELECT * FROM tbl_error_logging;`, ele retornará um registro com estes valores: *Erro encontrado*, *42703* e *A coluna "val" não existe em tbl\$11*.

O exemplo adicional do tratamento de erros a seguir usa um manipulador EXIT e um manipulador CONTINUE. Ele cria duas tabelas: uma tabela de dados e uma tabela de logs. Ele também cria um procedimento armazenado que demonstra o tratamento de erros:

```
CREATE TABLE tbl_1 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_2() NONATOMIC AS
$$
BEGIN
    INSERT INTO tbl_1 VALUES (1);
    BEGIN
        INSERT INTO tbl_1 VALUES (100);
        -- Expect an error for the insert statement following, because of the invalid value
        INSERT INTO tbl_1 VALUES ("val");
        INSERT INTO tbl_1 VALUES (101);
    EXCEPTION EXIT_HANDLER WHEN OTHERS THEN
        INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
    END;
    INSERT INTO tbl_1 VALUES (2);
    -- Expect an error for the insert statement following, because of the invalid value
    INSERT INTO tbl_1 VALUES ("val");
    INSERT INTO tbl_1 VALUES (3);
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

Depois de criar o procedimento armazenado, chame-o com o seguinte:

```
CALL sp_exc_handling_2();
```

Quando ocorre um erro no bloco de exceção interno, delimitado pelo conjunto interno de BEGIN e END, ele é tratado pelo manipulador EXIT. Qualquer erro ocorrido no bloco externo é tratado pelo manipulador CONTINUE. 

Depois de você chamar o procedimento armazenado de amostra, as tabelas conterão os seguintes registros:
+ Se você executar `SELECT * FROM tbl_1;`, ele retornará quatro registros, com os valores 1, 2, 3 e 100.
+ Se você executar `SELECT * FROM tbl_error_logging;`, ele retornará dois registros. Eles têm estes valores: *Erro encontrado*, *42703* e *A coluna "val" não existe em tbl\$11*.

Se a tabela **tbl\$1error\$1logging** não existir, ela vai gerar uma exceção.

O exemplo a seguir mostra como usar o manipulador de exceções CONTINUE com o loop FOR. Esse exemplo cria três tabelas e as usa em um loop FOR dentro de um procedimento armazenado. O loop FOR é uma variante do conjunto de resultados, o que significa que itera nos resultados de uma consulta:

```
CREATE TABLE tbl_1 (a int);
INSERT INTO tbl_1 VALUES (1), (2), (3);
CREATE TABLE tbl_2 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_loop() NONATOMIC AS
$$
DECLARE
 rec RECORD;
BEGIN
    FOR rec IN SELECT a FROM tbl_1
    LOOP
        IF rec.a = 2 THEN
            -- Expect an error for the insert statement following, because of the invalid value
            INSERT INTO tbl_2 VALUES("val");
        ELSE
            INSERT INTO tbl_2 VALUES (rec.a);
        END IF;
    END LOOP;
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

Chame o procedimento armazenado:

```
CALL sp_exc_handling_loop();
```

Depois de você chamar o procedimento armazenado de amostra, as tabelas conterão os seguintes registros:
+  Se você executar `SELECT * FROM tbl_2;`, ele retornará dois registros. Eles contêm os valores 1 e 3.
+ Se você executar `SELECT * FROM tbl_error_logging;`, ele retornará um registro com estes valores: *Erro encontrado*, *42703* e *A coluna "val" não existe em tbl\$12*.

Observações sobre o uso referente ao manipulador CONTINUE:
+ As palavras-chave CONTINUE\$1HANDLER e EXIT\$1HANDLER só podem ser usadas em procedimentos armazenados NONATOMIC.
+ As palavras-chave CONTINUE\$1HANDLER e EXIT\$1HANDLER são opcionais. EXIT\$1HANDLER é o padrão.

# Registro em log de procedimentos armazenados
<a name="c_PLpgSQL-logging"></a>

Este tópico descreve os procedimentos armazenados e as visualizações que o Amazon Redshift usa para o registro em log de procedimentos armazenados.

Detalhes sobre procedimentos armazenados são registrados em log nas seguintes tabelas e visualizações do sistema:
+ SVL\$1STORED\$1PROC\$1CALL - Os detalhes são registrados sobre a hora de início e de término da chamada de procedimento armazenado e se a chamada é encerrada antes da conclusão. Para obter mais informações, consulte [SVL\$1STORED\$1PROC\$1CALL](r_SVL_STORED_PROC_CALL.md).
+ SVL\$1STORED\$1PROC\$1MESSAGES - Mensagens em procedimentos armazenados emitidos pela consulta RAISE são registradas com o nível de registro em log correspondente. Para obter mais informações, consulte [SVL\$1STORED\$1PROC\$1MESSAGES](r_SVL_STORED_PROC_MESSAGES.md).
+ SVL\$1QLOG - O ID da consulta da chamada de procedimento é registrado para cada consulta chamada a partir de um procedimento armazenado. Para obter mais informações, consulte [SVL\$1QLOG](r_SVL_QLOG.md).
+ STL\$1UTILITYTEXT - Chamadas de procedimento armazenado são registradas em log após serem concluídas. Para obter mais informações, consulte [STL\$1UTILITYTEXT](r_STL_UTILITYTEXT.md).
+ PG\$1PROC\$1INFO - Esta visualização do catálogo do sistema mostra informações sobre procedimentos armazenados. Para obter mais informações, consulte [PG\$1PROC\$1INFO](r_PG_PROC_INFO.md).

# Limitações dos procedimentos armazenados
<a name="stored-procedure-constraints"></a>

Este tópico descreve as limitações dos procedimentos armazenados do Amazon Redshift.

As considerações a seguir se aplicam ao usar procedimentos armazenados do Amazon Redshift.

## Diferenças entre Amazon Redshift e PostgreSQL para suporte a procedimentos armazenados
<a name="stored-procedure-differences"></a>

 A seguir estão as diferenças entre o suporte a procedimentos armazenados no Amazon Redshift e PostgreSQL:
+ O Amazon Redshift não oferece suporte a subtransações e, portanto, tem suporte limitado para blocos de tratamento de exceção.

## Considerações e limitações
<a name="stored-procedure-limits"></a>

Veja a seguir as considerações sobre os procedimentos armazenados no Amazon Redshift:
+ O número máximo de procedimentos armazenados para um banco de dados é 10.000.
+ O tamanho máximo do código-fonte para um procedimento é 2 MB.
+ O número máximo de cursores explícitos e implícitos que podem ser abertos simultaneamente em uma sessão do usuário é 1. Loops FOR que iteram sobre o conjunto de resultados de uma instrução SQL abrem cursores implícitos. Não há suporte para cursores aninhados.
+ Os cursores explícitos e implícitos têm as mesmas restrições no tamanho do conjunto de resultados que os cursores padrão do Amazon Redshift. Para obter mais informações, consulte [Restrições de cursor](declare.md#declare-constraints). 
+ O número máximo de níveis para chamadas aninhadas é 16.
+ O número máximo de parâmetros de procedimento é 32 para argumentos de entrada e 32 para argumentos de saída.
+ O número máximo de variáveis em um procedimento armazenado é 1.024.
+ Qualquer comando SQL que requer seu próprio contexto de transação não é compatível dentro de um procedimento armazenado. Os exemplos incluem:
  + PREPARE
  + CREATE/DROP DATABASE
  + CREATE EXTERNAL TABLE
  + VACUUM
  + SET LOCAL
  + ALTER TABLE APPEND
+ A chamada do método `registerOutParameter` pelo driver do Java Database Connectivity (JDBC) não é compatível com o tipo de dados `refcursor`. Para obter um exemplo de uso do tipo de dados `refcursor`, consulte [Exibir um conjunto de resultados de um procedimento armazenado](stored-procedure-result-set.md).

# Referência da linguagem PL/pgSQL
<a name="c_pl_pgSQL_reference"></a>

Os procedimentos armazenados no Amazon Redshift são baseados na linguagem procedural PostgreSQL PL/pgSQL, com algumas diferenças importantes. Nesta referência, você pode encontrar detalhes da sintaxe PL/pgSQL implementada pelo Amazon Redshift. Para obter mais informações sobre PL/pgSQL, consulte [PL pgSQL - Linguagem procedural SQL](https://www.postgresql.org/docs/8.0/plpgsql.html) na documentação do PostgreSQL.

**Topics**
+ [Convenções de referência da PL/pgSQL](c_PL_reference_conventions.md)
+ [Estrutura da PL/pgSQL](c_PLpgSQL-structure.md)
+ [Instruções da PL/pgSQL compatíveis](c_PLpgSQL-statements.md)

# Convenções de referência da PL/pgSQL
<a name="c_PL_reference_conventions"></a>

Nesta seção, encontre as convenções que são usadas para escrever a sintaxe para a linguagem de procedimento armazenado PL/pgSQL. 

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/redshift/latest/dg/c_PL_reference_conventions.html)

# Estrutura da PL/pgSQL
<a name="c_PLpgSQL-structure"></a>

PL/pgSQL é uma linguagem procedural com muitas das mesmas construções de outras linguagens procedurais. 

**Topics**
+ [Bloquear](#r_PLpgSQL-block)
+ [Declaração de variáveis](#r_PLpgSQL-variable-declaration)
+ [Declaração de alias](#r_PLpgSQL-alias-declaration)
+ [Variáveis integradas](#r_PLpgSQL-builtin-variables)
+ [Tipos de registro](#r_PLpgSQL-record-type)

## Bloquear
<a name="r_PLpgSQL-block"></a>

PL/pgSQL é uma linguagem estruturada em blocos. O corpo completo de um procedimento é definido em um bloco, que contém declarações variáveis e instruções em PL/pgSQL. Uma instrução também pode ser um bloco aninhado, ou um sub-bloco. 

Finalize as declarações e as instruções com ponto-e-vírgula. Coloque um ponto-e-vírgula após a palavra-chave END de um bloco ou sub-bloco. Não use ponto-e-vírgula depois das palavras DECLARE e BEGIN. 

Todas as palavras-chave e os identificadores podem ser escritos com mistura entre maiúsculas e minúsculas. Os identificadores são convertidos implicitamente para minúsculas a menos que estejam entre aspas.

Um hífen duplo (--) inicia um comentário que se estende até o final da linha. Um /\$1 inicia um comentário de bloco que se estende até a próxima ocorrência de \$1/. Não é possível aninhar comentários de bloco. No entanto, é possível incluir comentários de hífen duplo em um comentário de bloco e um hífen duplo pode ocultar os delimitadores /\$1 e \$1/ do comentário de bloco.

Qualquer instrução na seção de instruções de um bloco pode ser um sub-bloco. Use sub-blocos para agrupamento lógico ou para localizar variáveis em um pequeno grupo de instruções.

```
[ <<label>> ]
[ DECLARE
  declarations ]
BEGIN
  statements
END [ label ];
```

As variáveis declaradas na seção de declarações que antecede um bloco são inicializadas com seus valores padrão toda vez que o bloco for inserido. Em outras palavras, não são inicializadas apenas uma vez por chamada da função.

Por exemplo:

```
CREATE PROCEDURE update_value() AS $$
DECLARE
  value integer := 20;
BEGIN
  RAISE NOTICE 'Value here is %', value;  -- Value here is 20
  value := 50;
  --
  -- Create a subblock
  --
  DECLARE
    value integer := 80;
  BEGIN
    RAISE NOTICE 'Value here is %', value;  -- Value here is 80
  END;

  RAISE NOTICE 'Value here is %', value;  -- Value here is 50
END;
$$ LANGUAGE plpgsql;
```

Use um rótulo para identificar o bloco a ser usado em uma instrução EXIT ou para qualificar os nomes das variáveis declaradas no bloco.

Não confunda o uso de BEGIN/END para instruções de agrupamento na PL/pgSQL com os comandos de banco de dados para controle de transações. BEGIN e END na PL/pgSQL destinam-se apenas para o agrupamento. Elas não iniciam ou encerram uma transação.

## Declaração de variáveis
<a name="r_PLpgSQL-variable-declaration"></a>

Declare todas as variáveis em um bloco, exceto as variáveis de loop, na seção DECLARE do bloco. As variáveis podem usar qualquer tipo de dados válido do Amazon Redshift. Para obter os tipos de dados compatíveis, consulte [Tipos de dados](c_Supported_data_types.md). 

As variáveis PL/pgSQL podem ser qualquer tipo de dados compatível com o Amazon Redshift, mais `RECORD` e `refcursor`. Para obter mais informações sobre o `RECORD`, consulte [Tipos de registro](#r_PLpgSQL-record-type). Para obter mais informações sobre o `refcursor`, consulte [Cursores](c_PLpgSQL-statements.md#r_PLpgSQL-cursors). 

```
DECLARE
name [ CONSTANT ] type [ NOT NULL ] [ { DEFAULT | := } expression ];
```

Veja a seguir exemplos de declarações de variáveis.

```
customerID integer;
numberofitems numeric(6);
link varchar;
onerow RECORD;
```

A variável de loop de um loop FOR iterando sobre uma gama de inteiros é automaticamente declarada como uma variável inteira. 

A cláusula DEFAULT, se fornecida, especifica o valor inicial atribuído à variável quando o bloco é inserido. Se a cláusula DEFAULT não for fornecida, a variável será inicializada com o valor SQL NULL. A opção CONSTANT impede que a variável seja atribuída, para que seu valor permaneça constante ao longo da duração do bloco. Se NOT NULL for especificado, uma atribuição de um valor nulo resultará em um erro de tempo de execução. Todas as variáveis declaradas como NOT NULL devem ter um valor padrão não nulo especificado.

O valor padrão é avaliado toda vez que o bloco for inserido. Por exemplo, atribuir `now()` a uma variável do tipo `timestamp` faz com que a variável tenha o horário da chamada de função atual, não o horário em que a função foi pré-compilada.

```
quantity INTEGER DEFAULT 32;
url VARCHAR := 'http://mysite.com';
user_id CONSTANT INTEGER := 10;
```

`refcursor` é o tipo de dados das variáveis de cursor dentro dos procedimento armazenados. Um valor `refcursor` pode ser retornado dentro de um procedimento armazenado. Para ter mais informações, consulte [Exibir um conjunto de resultados de um procedimento armazenado](stored-procedure-result-set.md).

## Declaração de alias
<a name="r_PLpgSQL-alias-declaration"></a>

Se a assinatura do procedimento armazenado omitir o nome do argumento, você pode declarar um alias para o argumento.

```
name ALIAS FOR $n;
```

## Variáveis integradas
<a name="r_PLpgSQL-builtin-variables"></a>

As seguintes variáveis integradas são compatíveis:
+ FOUND
+ SQLSTATE
+ SQLERRM
+ GET DIAGNOSTICS integer\$1var := ROW\$1COUNT;

FOUND é uma variável especial do tipo booliana. FOUND começa como false em cada chamada de procedimento. FOUND é definida pelos seguintes tipos de instruções:
+ SELECT INTO

  Define FOUND como true se retornar uma linha e false se não retornar linhas.
+ UPDATE, INSERT e DELETE

  Define FOUND como true se pelo menos uma linha for afetada e como false se nenhuma linha for afetada.
+ FETCH

  Define FOUND como true se retornar uma linha e false se não retornar linhas.
+ Instrução FOR

  Define FOUND como true se a instrução FOR iterar uma ou mais vezes, caso contrário, false. Isso se aplica às três variantes da instrução FOR: loops FOR de inteiros, loops FOR de conjunto de registros e loops FOR de conjunto de registros dinâmico. 

  FOUND é definida ao sair do loop FOR. Dentro do runtime do loop, FOUND não é modificada pela instrução FOR. No entanto, ela pode ser alterada pela execução de outras instruções no corpo do loop.

Por exemplo:

```
CREATE TABLE employee(empname varchar);
CREATE OR REPLACE PROCEDURE show_found()
AS  $$
DECLARE
  myrec record;
BEGIN
  SELECT INTO myrec * FROM employee WHERE empname = 'John';
  IF NOT FOUND THEN
    RAISE EXCEPTION 'employee John not found';
  END IF;
END;
$$ LANGUAGE plpgsql;
```

Dentro de um tratador de exceções, a variável especial SQLSTATE contém o código de erro que corresponde à exceção gerada. A variável especial SQLERRM contém a mensagem de erro associada à exceção. Essas variáveis não são definidas fora dos tratadores de exceções e exibem um erro se forem usadas.

Por exemplo:

```
CREATE OR REPLACE PROCEDURE sqlstate_sqlerrm() AS
$$
BEGIN
  UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
  EXECUTE 'select invalid';
  EXCEPTION WHEN OTHERS THEN
  RAISE INFO 'error message SQLERRM %', SQLERRM;
  RAISE INFO 'error message SQLSTATE %', SQLSTATE;
END;
$$ LANGUAGE plpgsql;
```

ROW\$1COUNT é usada com o comando GET DIAGNOSTICS. Ela mostra o número de linhas processadas pelo último comando SQL enviado para o mecanismo SQL.

Por exemplo:

```
CREATE OR REPLACE PROCEDURE sp_row_count() AS
$$
DECLARE
  integer_var int;
BEGIN
  INSERT INTO tbl_row_count VALUES(1);
  GET DIAGNOSTICS integer_var := ROW_COUNT;
  RAISE INFO 'rows inserted = %', integer_var;
END;
$$ LANGUAGE plpgsql;
```

## Tipos de registro
<a name="r_PLpgSQL-record-type"></a>

Um tipo RECORD não é um tipo de dados real, apenas um espaço reservado. Variáveis de tipo de registro assumem a estrutura real da linha à qual são atribuídas durante o comando SELECT ou FOR. A subestrutura de uma variável de registro pode mudar toda vez que receber um valor. Até uma variável de registro receber uma atribuição, ela não possui subestrutura. Qualquer tentativa de acessar um campo nela gerará um erro de tempo de execução.

```
name RECORD;
```

Por exemplo:

```
CREATE TABLE tbl_record(a int, b int);
INSERT INTO tbl_record VALUES(1, 2);
CREATE OR REPLACE PROCEDURE record_example()
LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
BEGIN
  FOR rec IN SELECT a FROM tbl_record
  LOOP
    RAISE INFO 'a = %', rec.a;
  END LOOP;
END;
$$;
```

# Instruções da PL/pgSQL compatíveis
<a name="c_PLpgSQL-statements"></a>

 As instruções da PL/pgSQL aumentam comandos SQL com construções processuais, incluindo loops e expressões condicionais, a fim de controlar o fluxo lógico. A maioria dos comandos SQL pode ser usada, incluindo linguagem de manipulação de dados (DML), como COPY, UNLOAD e INSERT, e linguagem de definição de dados (DDL), como CREATE TABLE. Para obter a lista dos comandos SQL abrangentes, consulte [Comandos SQL](c_SQL_commands.md). Além disso, as seguintes instruções PL/pgSQL são compatíveis com o Amazon Redshift. 

**Topics**
+ [Atribuição](#r_PLpgSQL-assignment)
+ [SELECT INTO](#r_PLpgSQL-select-into)
+ [No-op](#r_PLpgSQL-no-op)
+ [SQL dinâmico](#r_PLpgSQL-dynamic-sql)
+ [Return](#r_PLpgSQL-return)
+ [Condicionais: IF](#r_PLpgSQL-conditionals-if)
+ [Condicionais: CASE](#r_PLpgSQL-conditionals-case)
+ [Loops](#r_PLpgSQL-loops)
+ [Cursores](#r_PLpgSQL-cursors)
+ [RAISE](#r_PLpgSQL-messages-errors)
+ [Controle da transação](#r_PLpgSQL-transaction-control)

## Atribuição
<a name="r_PLpgSQL-assignment"></a>

A instrução de atribuição atribui um valor a uma variável. A expressão deve retornar um único valor.

```
identifier := expression;
```

Usar o `=` não padrão para a atribuição, em vez do `:=`, também é aceito.

Se o tipo de dados da expressão não corresponder ao tipo de dados da variável ou a variável tiver um tamanho ou uma precisão, o valor do resultado será convertido implicitamente.

Veja a seguir alguns exemplos.

```
customer_number := 20;
tip := subtotal * 0.15;
```

## SELECT INTO
<a name="r_PLpgSQL-select-into"></a>

A instrução SELECT INTO atribui o resultado de várias colunas (mas somente uma linha) a uma variável de registro ou uma lista de variáveis escalares.

```
SELECT INTO target select_expressions FROM ...;
```

Na sintaxe anterior, *target* pode ser uma variável de registro ou uma lista separada por vírgulas de variáveis simples e campos de registro. A lista *select\$1expressions* e o restante do comando são iguais ao SQL regular.

Se uma lista de variáveis for usada como *target*, os valores selecionados deverão corresponder exatamente à estrutura do destino, caso contrário, ocorrerá um erro de tempo de execução. Quando uma variável de registro for o destino, ela se configura automaticamente para o tipo de linha das colunas do resultado da consulta.

A cláusula INTO pode aparecer praticamente em qualquer lugar na instrução SELECT. Normalmente ela aparece logo após a cláusula SELECT ou imediatamente antes da cláusula FROM. Ou seja, ela aparece imediatamente antes ou logo após a lista *select\$1expressions*.

Se a consulta não retornar nenhuma linha, valores NULL serão atribuídos a *target*. Se a consulta retornar várias linhas, a primeira linha será atribuída a *target* e o restante é descartado. A menos que a instrução contenha um ORDER BY, a primeira linha é não determinística.

Para determinar se a atribuição retornou pelo menos uma fila, use a variável especial FOUND.

```
SELECT INTO customer_rec * FROM cust WHERE custname = lname;
IF NOT FOUND THEN
  RAISE EXCEPTION 'employee % not found', lname;
END IF;
```

Para testar se um resultado de registro é nulo, use o condicional IS NULL. Não é possível determinar se outras linhas adicionais foram descartadas. O exemplo a seguir lida com o caso em que nenhuma linha foi retornada.

```
CREATE OR REPLACE PROCEDURE select_into_null(return_webpage OUT varchar(256))
AS $$
DECLARE
  customer_rec RECORD;
BEGIN
  SELECT INTO customer_rec * FROM users WHERE user_id=3;
  IF customer_rec.webpage IS NULL THEN
    -- user entered no webpage, return "http://"
    return_webpage = 'http://';
  END IF;
END;
$$ LANGUAGE plpgsql;
```

## No-op
<a name="r_PLpgSQL-no-op"></a>

A instrução no-op (`NULL;`) é uma instrução de espaço reservado que não faz nada. Uma instrução no-op pode indicar que uma ramificação de uma cadeia IF-THEN-ELSE está vazia.

```
NULL;
```

## SQL dinâmico
<a name="r_PLpgSQL-dynamic-sql"></a>

Para gerar comandos dinâmicos que podem involver diferentes tabelas ou diferentes tipos de dados toda vez que forem executados a partir de um procedimento armazenado em PL/pgSQL, use a instrução `EXECUTE`.

```
EXECUTE command-string [ INTO target ];
```

Acima, *command-string* é uma expressão que gera uma string (do tipo texto) que contém o comando a ser executado. Esse valor de *command-string* é enviado ao mecanismo SQL. Nenhuma substituição das variáveis em PL/pgSQL é feita na string de comando. Os valores das variáveis deve ser inserido na string de comando à medida que é construída.

**nota**  
Não é possível usar as instruções COMMIT e ROLLBACK em SQL dinâmico. Para obter informações sobre como usar as instruções COMMIT e ROLLBACK em um procedimento armazenado, consulte [Gerenciamento de transações](stored-procedure-transaction-management.md). 

Ao trabalhar com comandos dinâmicos, é usual precisar lidar com o escape de aspas simples. Recomendamos colocar texto fixo entre aspas no corpo da função usando cotação de dólar. Valores dinâmicos a serem inseridos em uma consulta construída exigem tratamento especial, pois eles mesmos podem conter aspas. O exemplo a seguir assume cotação de dólar para a função como um todo, para que as aspas não precisem ser duplicadas.

```
EXECUTE 'UPDATE tbl SET '
  || quote_ident(colname)
  || ' = '
  || quote_literal(newvalue)
  || ' WHERE key = '
  || quote_literal(keyvalue);
```

O exemplo anterior mostra as funções `quote_ident(text)` e `quote_literal(text)`. Esse exemplo envia variáveis que contém identificadores de coluna e tabela para a função `quote_ident`. Ele também envia variáveis que contém strings literais no comando construído para a função `quote_literal`. Ambas as funções executam as etapas apropriadas para retornar o texto de entrada entre aspas duplas ou simples, respectivamente, com quaisquer caracteres especiais incorporados com escape adequado.

A cotação de dólar só é útil para a cotação de texto fixo. Não grave o exemplo anterior no formato a seguir.

```
EXECUTE 'UPDATE tbl SET '
  || quote_ident(colname)
  || ' = $$'
  || newvalue
  || '$$ WHERE key = '
  || quote_literal(keyvalue);
```

Não faça isso pois o exemplo trava se o conteúdo de `newvalue` conter \$1\$1. O mesmo problema se aplica a qualquer outro delimitador de cotação de dólar que venha a escolher. Para citar com segurança um texto não conhecido previamente, use a função `quote_literal`.

## Return
<a name="r_PLpgSQL-return"></a>

A instrução RETURN retorna para o chamador a partir de um procedimento armazenado.

```
RETURN;
```

Por exemplo:

```
CREATE OR REPLACE PROCEDURE return_example(a int)
AS $$  
BEGIN
  FOR b in 1..10 LOOP
    IF b < a THEN
      RAISE INFO 'b = %', b;
    ELSE
      RETURN;
    END IF;
  END LOOP;
END;
$$ LANGUAGE plpgsql;
```

## Condicionais: IF
<a name="r_PLpgSQL-conditionals-if"></a>

A instrução condicional IF pode assumir as seguintes formas na linguagem PL/pgSQL que o Amazon Redshift usa:
+ IF ... THEN

  ```
  IF boolean-expression THEN
    statements
  END IF;
  ```

  Por exemplo:

  ```
  IF v_user_id <> 0 THEN
    UPDATE users SET email = v_email WHERE user_id = v_user_id;
  END IF;
  ```
+ IF ... THEN ... ELSE

  ```
  IF boolean-expression THEN
    statements
  ELSE
    statements
  END IF;
  ```

  Por exemplo:

  ```
  IF parentid IS NULL OR parentid = ''
  THEN
    return_name = fullname;
    RETURN;
  ELSE
    return_name = hp_true_filename(parentid) || '/' || fullname;
    RETURN;
  END IF;
  ```
+ IF ... THEN ... ELSIF ... THEN ... ELSE 

  A palavra-chave ELSIF também pode ser escrita como ELSEIF.

  ```
  IF boolean-expression THEN
    statements
  [ ELSIF boolean-expression THEN
    statements
  [ ELSIF boolean-expression THEN
    statements
      ...] ]
  [ ELSE
    statements ]
  END IF;
  ```

  Por exemplo:

  ```
  IF number = 0 THEN
    result := 'zero';
  ELSIF number > 0 THEN
    result := 'positive';
  ELSIF number < 0 THEN
    result := 'negative';
  ELSE
    -- the only other possibility is that number is null
    result := 'NULL';
  END IF;
  ```

## Condicionais: CASE
<a name="r_PLpgSQL-conditionals-case"></a>

A instrução condicional CASE pode assumir as seguintes formas na linguagem PL/pgSQL que o Amazon Redshift usa:
+ CASE simples 

  ```
  CASE search-expression
  WHEN expression [, expression [ ... ]] THEN
    statements
  [ WHEN expression [, expression [ ... ]] THEN
    statements
    ... ]
  [ ELSE
    statements ]
  END CASE;
  ```

  Uma instrução CASE simples oferece execução condicional com base na igualdade dos operandos.

  O valor *search-expression* é avaliado uma vez e comparado sucessivamente com cada *expression* nas cláusulas WHEN. Se uma correspondência for encontrada, as *statements* correspondentes são executadas e o controle passa para a próxima instrução após END CASE. As expressões WHEN subsequentes não são avaliadas. Se nenhuma correspondência for encontrada, as *statements* ELSE são executadas. No entanto, se ELSE não estiver presente, uma exceção CASE\$1NOT\$1FOUND será gerada.

  Por exemplo:

  ```
  CASE x
  WHEN 1, 2 THEN
    msg := 'one or two';
  ELSE
    msg := 'other value than one or two';
  END CASE;
  ```
+ CASE pesquisado 

  ```
  CASE
  WHEN boolean-expression THEN
    statements
  [ WHEN boolean-expression THEN
    statements
    ... ]
  [ ELSE
    statements ]
  END CASE;
  ```

  A formato pesquisado de CASE oferece execução condicional baseada na veracidade de expressões boolianas. 

  A *boolean-expression* de cada cláusula WHEN é, por sua vez, avaliada até uma verdadeira ser encontrada. Depois, as instruções correspondentes são executadas e o controle passa para a próxima instrução após END CASE. As *expressions* WHEN subsequentes não são avaliadas. Se nenhum resultado verdadeiro for encontrado, as *statements* ELSE são executadas. No entanto, se ELSE não estiver presente, uma exceção CASE\$1NOT\$1FOUND será gerada.

  Por exemplo:

  ```
  CASE
  WHEN x BETWEEN 0 AND 10 THEN
    msg := 'value is between zero and ten';
  WHEN x BETWEEN 11 AND 20 THEN
    msg := 'value is between eleven and twenty';
  END CASE;
  ```

## Loops
<a name="r_PLpgSQL-loops"></a>

As instruções de loop podem assumir os seguintes formatos na linguagem PL/pgSQL que o Amazon Redshift usa:
+ Loop simples 

  ```
  [<<label>>]
  LOOP
    statements
  END LOOP [ label ];
  ```

  Um loop simples define um loop não condicional que é repetido indefinidamente até ser encerrado por uma instrução EXIT ou RETURN. O rótulo opcional pode ser usado pelas instruções EXIT e CONTINUE dentro de loops aninhados para especificar a qual loop as instruções EXIT e CONTINUE se referem.

  Por exemplo:

  ```
  CREATE OR REPLACE PROCEDURE simple_loop()
  LANGUAGE plpgsql
  AS $$
  BEGIN
    <<simple_while>>
    LOOP
      RAISE INFO 'I am raised once';  
      EXIT simple_while;
      RAISE INFO 'I am not raised';
    END LOOP;
    RAISE INFO 'I am raised once as well';
  END;
  $$;
  ```
+ Loop de saída

  ```
  EXIT [ label ] [ WHEN expression ];
  ```

  Se *label* não estiver presente, o loop mais interno será encerrado e a instrução após END LOOP será executada na sequência. Se *label* estiver presente, deverá ser o rótulo do loop atual ou de algum outro nível de loop ou bloco aninhado. Depois, o loop ou bloco nomeado será encerrado e o controle continuará com a instrução após END do loop ou bloco correspondente.

  Se WHEN for especificada, a saída do loop ocorrerá apenas se a *expression* for verdadeira. Caso contrário, o controle passa para a instrução após EXIT.

  Você pode usar EXIT com todos os tipos de loops, seu uso não se limita a loops não condicionais.

  Quando usado com um bloco BEGIN, EXIT passa o controle para a próxima instrução após o final do bloco. Um rótulo pode ser usado para essa finalidade. Um EXIT sem rótulo nunca corresponderá a um bloco BEGIN.

  Por exemplo:

  ```
  CREATE OR REPLACE PROCEDURE simple_loop_when(x int)
  LANGUAGE plpgsql
  AS $$
  DECLARE i INTEGER := 0;
  BEGIN
    <<simple_loop_when>>
    LOOP
      RAISE INFO 'i %', i;
      i := i + 1;
      EXIT simple_loop_when WHEN (i >= x);
    END LOOP;
  END;
  $$;
  ```
+ Loop de continuação 

  ```
  CONTINUE [ label ] [ WHEN expression ];
  ```

  Se *label* não for fornecido, a execução pulará para a próxima iteração do loop mais interno. Ou seja, todas as instruções restantes no corpo do loop serão ignoradas. Depois, o controle retornará para a expressão de controle do loop (se houver) a fim de determinar se há necessidade de outra iteração do loop. Se *label* estiver presente, ele especifica o rótulo do loop cuja execução será continuada.

  Se WHEN for especificado, a próxima iteração do loop começará somente se a *expression* for verdadeira. Caso contrário, o controle passa para a instrução após CONTINUE.

  Você pode usar CONTINUE com todos os tipos de loops, seu uso não se limita a loops não condicionais.

  ```
  CONTINUE mylabel;
  ```
+ Loop WHILE 

  ```
  [<<label>>]
  WHILE expression LOOP
    statements
  END LOOP [ label ];
  ```

  A instrução WHILE repete uma sequência de instruções até a *boolean-expression* ser avaliada como verdadeira. A expressão é verificada imediatamente antes de cada entrada no corpo do loop.

  Por exemplo:

  ```
  WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
    -- some computations here
  END LOOP;
  
  WHILE NOT done LOOP
    -- some computations here
  END LOOP;
  ```
+ Loop FOR (variante inteiro) 

  ```
  [<<label>>]
  FOR name IN [ REVERSE ] expression .. expression LOOP
    statements
  END LOOP [ label ];
  ```

  O loop FOR (variante inteiro) cria um loop que itera sobre um intervalo de valores inteiros. O nome da variável é definido automaticamente como o tipo inteiro e exite somente dentro do loop. Qualquer definição existente do nome da variável será ignorada dentro do loop. As duas expressões que fornecem os limites superior e inferior do intervalo são avaliadas uma vez ao entrar no loop. Se você especificar REVERSE, o valor da etapa será subtraído, em vez de adicionado, após cada iteração.

  Se o limite inferior for maior que o limite superior (ou menor no caso REVERSE), o corpo do loop não será executado. Nenhum erro será gerado.

  Se um rótulo for anexado ao loop FOR, você pode referenciar a variável de loop inteira com um nome qualificado, usando esse rótulo.

  Por exemplo:

  ```
  FOR i IN 1..10 LOOP
    -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop
  END LOOP;
  
  FOR i IN REVERSE 10..1 LOOP
    -- i will take on the values 10,9,8,7,6,5,4,3,2,1 within the loop
  END LOOP;
  ```
+ Loop FOR (variante conjunto de resultados) 

  ```
  [<<label>>]
  FOR target IN query LOOP
    statements
  END LOOP [ label ];
  ```

  O *target* é uma variável de registro ou lista separada por vírgulas de variáveis escalares. O destino recebe todas as linhas resultantes da consulta e o corpo do loop é executado para cada linha.

  O loop FOR (variante conjunto de resultados) habilita a iteração de um procedimento armazenado pelos resultados de uma consulta e manipula os dados de acordo.

  Por exemplo:

  ```
  CREATE PROCEDURE cs_refresh_reports() AS $$
  DECLARE
    reports RECORD;
  BEGIN
    FOR reports IN SELECT * FROM cs_reports ORDER BY sort_key LOOP
      -- Now "reports" has one record from cs_reports
      EXECUTE 'INSERT INTO ' || quote_ident(reports.report_name) || ' ' || reports.report_query;
    END LOOP;
    RETURN;
  END;
  $$ LANGUAGE plpgsql;
  ```
+ Loop FOR com SQL dinâmico

  ```
  [<<label>>]
  FOR record_or_row IN EXECUTE text_expression LOOP 
    statements
  END LOOP;
  ```

  Um loop FOR com SQL dinâmico habilita a iteração de um procedimento armazenado pelos resultados de uma consulta dinâmica e manipula os dados de acordo.

  Por exemplo:

  ```
  CREATE OR REPLACE PROCEDURE for_loop_dynamic_sql(x int)
  LANGUAGE plpgsql
  AS $$
  DECLARE
    rec RECORD;
    query text;
  BEGIN
    query := 'SELECT * FROM tbl_dynamic_sql LIMIT ' || x;
    FOR rec IN EXECUTE query
    LOOP
      RAISE INFO 'a %', rec.a;
    END LOOP;
  END;
  $$;
  ```

## Cursores
<a name="r_PLpgSQL-cursors"></a>

Em vez de executar uma consulta completa de uma só vez, é possível definir um cursor. Um *cursor *encapsula uma consulta e lê o resultado da consulta algumas linhas por vez. Um dos motivos para fazer isso é evitar o estouro de memória quando o resultado contiver um grande número de linhas. Outro motivo é retornar a referência a um cursor criado por um procedimento armazenado, o que permite ao chamador ler as linhas. Essa abordagem oferece uma maneira eficiente de retornar grandes conjuntos de linhas de procedimentos armazenados.

Para usar cursores em um procedimento armazenado NONATOMIC, coloque o loop do cursor entre START TRANSACTION...COMMIT.

Para configurar um cursor, primeiro declare uma variável de cursor. Todo o acesso a cursores na PL/pgSQL passa por variáveis de cursor, que sempre são do tipo de dados especial `refcursor`. Um tipo de dados `refcursor` apenas mantém uma referência a um cursor. 

Você pode criar uma variável de cursor declarando-a como uma variável do tipo `refcursor`. Ou, use a sintaxe de declaração de cursor a seguir.

```
name CURSOR [ ( arguments ) ] FOR query ;
```

No trecho acima, *arguments* (se especificado) é uma lista separada por vírgulas de pares *name datatype* em que cada um define nomes a serem substituídos por valores de parâmetros em *query*. Os valores reais que substituirão esses nomes serão especificados posteriormente, quando o cursor for aberto.

Veja a seguir alguns exemplos.

```
DECLARE
  curs1 refcursor;
  curs2 CURSOR FOR SELECT * FROM tenk1;
  curs3 CURSOR (key integer) IS SELECT * FROM tenk1 WHERE unique1 = key;
```

Essas três variáveis têm o tipo de dados `refcursor`, mas a primeira pode ser usada com qualquer consulta. Por outro lado, a segunda tem uma consulta totalmente especificada já associada a ela, e a última tem uma consulta parametrizada associada a ela. O valor `key` é substituído por um valor de parâmetro inteiro quando o cursor é aberto. Diz-se que a variável `curs1` é *não associada *pois ela não está vinculada a nenhuma consulta específica.

Antes de usar um cursor para recuperar linhas, ele deve ser aberto. PL/pgSQL possui três formas da instrução OPEN, das quais duas usam variáveis de cursor não associadas e a terceira usa uma variável de cursor associada:
+ Abrir para seleção: a variável de cursor é aberta e recebe a consulta especificada para execução. O cursor não pode já estar aberto. Além disso, ele deverá ter sido declarado como um cursor não associado (isto é, como uma variável `refcursor` simples). A consulta SELECT é tratada da mesma maneira que outras instruções SELECT na PL/pgSQL. 

  ```
  OPEN cursor_name FOR SELECT ...;                     
  ```

  Por exemplo:

  ```
  OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;    
  ```
+ Abrir para execução: a variável de cursor é aberta e recebe a consulta especificada para execução. O cursor não pode já estar aberto. Além disso, ele deverá ter sido declarado como um cursor não associado (isto é, como uma variável `refcursor` simples). A consulta é especificada como uma expressão de string da mesma maneira que no comando EXECUTE. Essa abordagem oferece flexibilidade para que a consulta possa variar de uma execução para a próxima.

  ```
  OPEN cursor_name FOR EXECUTE query_string;
  ```

  Por exemplo:

  ```
  OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1);
  ```
+ Abrir um cursor associado: essa forma de OPEN é usada para abrir uma variável de cursor cuja consulta foi associada a ela no momento em que foi declarada. O cursor não pode já estar aberto. Uma lista das expressões de valor de argumento reais deverá aparecer se, e somente se, o cursor tiver sido declarado para receber argumentos. Esses valores são substituídos na consulta. 

  ```
  OPEN bound_cursor_name [ ( argument_values ) ];
  ```

  Por exemplo:

  ```
  OPEN curs2;
  OPEN curs3(42);
  ```

Depois que um cursor tiver sido aberto, trabalhe com ele usando as instruções descritas a seguir. Essas instruções não precisam ocorrer no mesmo procedimento armazenado que abriu o cursor. Você pode retornar um valor `refcursor` fora de um procedimento armazenado e deixar o chamador operar no cursor. Todos os portais são implicitamente fechados ao final da transação. Portanto, você pode usar um valor `refcursor` para fazer referência a um cursor aberto somente até o final da transação.
+ FETCH recupera a próxima linha do cursor em um destino. Esse destino pode ser uma variável de linha, uma variável de registro ou uma lista separada por vírgulas de variáveis simples, igual à SELECT INTO. Como na SELECT INTO, é possível verificar a variável especial FOUND para conferir se uma linha foi obtida.

  ```
  FETCH cursor INTO target;
  ```

  Por exemplo:

  ```
  FETCH curs1 INTO rowvar;
  ```
+ CLOSE fecha o portal subjacente a um cursor aberto. Use essa instrução para liberar recursos antes do fim da transação. Você também pode usar essa instrução a fim de liberar a variável de cursor para que possa ser aberta novamente.

  ```
  CLOSE cursor;
  ```

  Por exemplo:

  ```
  CLOSE curs1;
  ```

## RAISE
<a name="r_PLpgSQL-messages-errors"></a>

Use a instrução `RAISE level` para relatar mensagens e gerar erros.

```
RAISE level 'format' [, variable [, ...]];
```

Os níveis possíveis são NOTICE, INFO, LOG, WARNING e EXCEPTION. EXCEPTION gera um erro, que normalmente cancela a transação atual. Os outros níveis geram apenas mensagens com diferentes níveis de prioridade. 

Dentro da string de formato, % é substituído pela representação de string do próximo argumento opcional. Escreva %% para emitir um % literal. No momento, argumentos opcionais devem ser variáveis simples, não expressões, e o formato deve ser uma string literal simples.

No exemplo a seguir, o valor de `v_job_id` substitui o % na string.

```
RAISE NOTICE 'Calling cs_create_job(%)', v_job_id;
```

Use a instrução `RAISE` para relançar a exceção capturada por um bloco de tratamento de exceções. Essa instrução só é válida em blocos de tratamento de exceções de procedimentos armazenados no modo NONATOMIC.

```
RAISE;
```

## Controle da transação
<a name="r_PLpgSQL-transaction-control"></a>

Você pode trabalhar com instruções de controle de transação na linguagem PL/pgSQL que o Amazon Redshift usa. Para obter informações sobre como usar as instruções COMMIT, ROLLBACK e TRUNCATE em um procedimento armazenado, consulte [Gerenciamento de transações](stored-procedure-transaction-management.md). 

Nos procedimentos armazenados no modo NONATOMIC, use `START TRANSACTION` para iniciar um bloco de transação.

```
START TRANSACTION;
```

**nota**  
A instrução PL/pgSQL START TRANSACTION é diferente do comando SQL START TRANSACTION das seguintes formas:  
Nos procedimentos armazenados, START TRANSACTION não é sinônimo de BEGIN.
A instrução PL/pgSQL não é compatível com palavras-chave opcionais de nível de isolamento e permissão de acesso.