O módulo de linguagem PL/Python importa
automaticamente um módulo Python chamado
plpy.
As funções e constantes neste módulo estão disponíveis no código
Python como
plpy..
xxx
O módulo plpy fornece várias funções para
executar comandos de banco de dados:
plpy.execute(consulta [, limite])
Chamar plpy.execute com uma cadeia de
caracteres de consulta, e um argumento opcional de limite de
linhas, faz com que esta consulta seja executada, e o resultado
seja retornado em um objeto de resultado.
Se o limite especificado for maior que
zero, então plpy.execute irá trazer no máximo
limite linhas, como se a consulta
incluísse uma cláusula LIMIT.
Omitir o limite ou especificá-lo como
zero resulta em nenhum limite de linhas.
O objeto de resultado emula uma lista ou objeto de dicionário. O objeto de resultado pode ser acessado pelo número da linha e nome da coluna. Por exemplo:
res = plpy.execute("SELECT * FROM minha_tabela", 5)
retorna até 5 linhas de minha_tabela.
Se minha_tabela tiver a coluna
minha_coluna, esta coluna poderá ser acessada
via:
foo = res[i]["minha_coluna"]
O número de linhas retornadas pode ser obtido usando a função
integrada len.
O objeto de resultado possui estes métodos adicionais:
nrows()
Retorna o número de linhas processadas pelo comando.
Note que não é necessariamente igual ao número de linhas
retornadas.
Por exemplo, o comando UPDATE irá definir
este valor, mas não retornará nenhuma linha
(a menos que seja usado RETURNING).
status()
O valor retornado por SPI_execute().
colnames()coltypes()coltypmods()Retorna uma lista com os nomes das colunas, uma lista com os OIDs dos tipos de dados das colunas e uma lista com os modificadores específicos para os tipos de dados das colunas, respectivamente.
Estes métodos lançam uma exceção quando chamados em um
objeto de resultado de um comando que não produziu um
conjunto de resultados, por exemplo,
UPDATE sem RETURNING,
ou DROP TABLE.
Mas não há problema em usar estes métodos em um conjunto de
resultados contendo zero linhas.
__str__()
O método padrão __str__ é definido para
ser possível, por exemplo, depurar os resultados da
execução da consulta usando plpy.debug(res).
O objeto de resultado pode ser modificado.
Note que chamar plpy.execute fará com que todo
o conjunto de resultados seja lido na memória.
Esta função deve ser usada apenas quando se tiver certeza de que
o conjunto de resultados será relativamente pequeno.
Se não se desejar correr o risco do uso excessivo de memória
ao obter resultados grandes, deve ser usado
plpy.cursor em vez de
plpy.execute.
plpy.prepare(consulta [, tipos_de_dados_dos_argumentos])plpy.execute(plano [, argumentos [, limite]])
A instrução plpy.prepare prepara o plano de
execução para a consulta.
É chamada com uma cadeia de caracteres de consulta, e uma lista
contendo os tipos de dados dos parâmetros, se houver referências
a parâmetros na consulta. Por exemplo:
plano = plpy.prepare("SELECT sobrenome FROM usuarios WHERE nome = $1", ["text"])
onde text é o tipo de dados da variável
a ser passada para $1.
O segundo argumento é opcional se não se quiser passar nenhum
parâmetro para a consulta.
Após preparar a instrução, é usada a variante da função
plpy.execute para executá-la:
res = plpy.execute(plano, [nome], 5)
O plano é passado como primeiro argumento (em vez da cadeia de caracteres de consulta), e uma lista de valores para substituir na consulta como segundo argumento. O segundo argumento é opcional, se a consulta não tiver nenhum parâmetro. O terceiro argumento é o limite de linhas opcional, como antes.
Como alternativa, pode-se chamar o método
execute no objeto do plano:
rv = plan.execute(["nome"], 5)
Os parâmetros da consulta, e os campos das linhas de resultado, são convertidos entre os tipos de dados do PostgreSQL e do Python, conforme descrito em Valores dos dados.
Quando se prepara um plano usando o módulo
PL/Python, ele é salvo automaticamente.
Leia a documentação do SPI
(Interface de programação servidor) para obter uma descrição do que isto
significa.
Para fazer uso efetivo dessa funcionalidade nas chamadas de função,
é necessário usar um dos dicionários de armazenamento persistente
SD ou GD
(veja Compartilhamento de dados). Por exemplo:
CREATE FUNCTION usesavedplan()
RETURNS trigger
AS $$
if "plan" in SD:
plan = SD["plan"]
else:
plan = plpy.prepare("SELECT 1")
SD["plan"] = plan
# rest of function
$$ LANGUAGE plpython3u;
plpy.cursor(consulta)plpy.cursor(plano [, argumentos])
A função plpy.cursor recebe os mesmos argumentos
que plpy.execute (exceto pelo limite de linhas),
e retorna um objeto de cursor, que permite processar grandes
conjuntos de resultados em blocos menores.
Assim como acontece com plpy.execute, pode ser
usada uma cadeia de caracteres de consulta, ou um objeto de plano,
junto com uma lista de argumentos, ou a função
cursor pode ser chamada como um método do
objeto de plano.
O objeto de cursor fornece o método fetch, que
recebe como parâmetro um número inteiro,
e retorna um objeto de resultado.
A cada vez que é chamado o método fetch,
o objeto retornado conterá o próximo lote de linhas,
nunca maior que o valor do parâmetro.
Assim que todas as linhas tiverem sido buscadas, o método
fetch começará a retornar um objeto de
resultado vazio.
Os objetos de cursor também fornecem uma
interface de iterador que busca uma linha de cada vez,
até que todas as linhas tenham sido buscadas.
Os dados buscados dessa forma não são retornados como objetos de
resultado, mas sim como dicionários, com cada dicionário
correspondendo a uma única linha de resultado.
Um exemplo de duas maneiras de processar dados de uma tabela grande é:
CREATE FUNCTION count_odd_iterator()
RETURNS integer
AS $$
odd = 0
for row in plpy.cursor("select num from largetable"):
if row['num'] % 2:
odd += 1
return odd
$$ LANGUAGE plpython3u;
CREATE FUNCTION count_odd_fetch(batch_size integer)
RETURNS integer
AS $$
odd = 0
cursor = plpy.cursor("select num from largetable")
while True:
rows = cursor.fetch(batch_size)
if not rows:
break
for row in rows:
if row['num'] % 2:
odd += 1
return odd
$$ LANGUAGE plpython3u;
CREATE FUNCTION count_odd_prepared()
RETURNS integer
AS $$
odd = 0
plan = plpy.prepare("select num from largetable where num % $1 <> 0", ["integer"])
rows = list(plpy.cursor(plan, [2])) # or: = list(plan.cursor([2]))
return len(rows)
$$ LANGUAGE plpython3u;
Os cursores são descartados automaticamente.
Mas se for desejado liberar explicitamente todos os recursos
mantidos por um cursor, deverá ser usado o método
close.
Uma vez fechado, o cursor não poderá mais ser buscado.
Não devem ser confundidos os objetos criados por
plpy.cursor, com os objetos da
DB-API definidos pelo
PEP 249.
Eles não possuem nada em comum, exceto o nome.
As funções que acessam o banco de dados podem encontrar erros,
o que fará com que sejam interrompidas e levantada uma exceção.
Tanto plpy.execute quanto
plpy.prepare podem levantar uma instância
de uma subclasse de plpy.SPIError,
que, por padrão, vai interromper a função.
Este erro pode ser tratado como qualquer outra exceção do
Python, usando a construção
try/except.
Por exemplo:
CREATE FUNCTION try_adding_joe()
RETURNS text
AS $$
try:
plpy.execute("INSERT INTO users(username) VALUES ('joe')")
except plpy.SPIError:
return "algo deu errado"
else:
return "Joe adicionado"
$$ LANGUAGE plpython3u;
A classe real da exceção lançada corresponde à condição específica
que causou o erro.
Veja a Tabela de códigos de erro do PostgreSQL para obter uma lista das
possíveis condições.
O módulo plpy.spieexceptions define uma classe de
exceção para cada condição do PostgreSQL,
derivando seus nomes do nome da condição.
Por exemplo, division_by_zero
se torna DivisionByZero,
unique_violation
se torna UniqueViolation,
fdw_error
se torna FdwError, e assim por diante.
Cada uma dessas classes de exceção herda de SPIError.
Esta separação facilita o tratamento de erros específicos.
Por exemplo:
CREATE FUNCTION insert_fraction(numerator int, denominator int)
RETURNS text
AS $$
from plpy import spiexceptions
try:
plan = plpy.prepare("INSERT INTO fractions (frac) VALUES ($1 / $2)", ["int", "int"])
plpy.execute(plan, [numerator, denominator])
except spiexceptions.DivisionByZero:
return "o denominador não pode ser igual a zero."
except spiexceptions.UniqueViolation:
return "já existe essa fração"
except plpy.SPIError as e:
return "outro erro, SQLSTATE %s" % e.sqlstate
else:
return "fraction inserted"
$$ LANGUAGE plpython3u;
Note que, como todas as exceções do módulo
plpy.spieexceptions herdam de
SPIError, uma cláusula except
para tratá-lo detectará qualquer erro de acesso ao banco de dados.
Como uma forma alternativa para lidar com diferentes condições de
erro, pode-se capturar a exceção SPIError,
e determinar a condição de erro específica dentro do bloco
except, observando o atributo
sqlstate do objeto de exceção.
Este atributo é um valor cadeia de caracteres, contendo o código
de erro “SQLSTATE”.
Esta abordagem fornece aproximadamente a mesma funcionalidade.