Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PL/Python: Programando em Python no PostgreSQL

PL/Python: Programando em Python no PostgreSQL

PL/Python é a implementação de Python como linguagem procedural no PostgreSQL.

Permite utilizar todas as bibliotecas de Python instaladas no sistema operacional.

PL/Python proporciona uma gama de possibilidades de recursos maior ao banco de dados graças às bibliotecas Python que permite fazer operações que transcendem os limites da base de dados.

Pode-se por exemplo com uma função PL/Python fazer raspagem de dados e jogá-los diretamente no banco (e pelo banco).

Juliano Atanazio

January 25, 2018
Tweet

More Decks by Juliano Atanazio

Other Decks in Programming

Transcript

  1. 2/52 About me Juliano Atanazio • Graduated in Computer Science

    for Business Management (Informática para Gestão de Negócios), FATEC Zona Sul, São Paulo – SP; • PostgreSQL DBA; • Linux admin; • Instructor (PostgreSQL); • LPIC-1, LPIC-2 Certified; • Linux user since 2000; • Free Software enthusiast; • Favorite technologies: PostgreSQL, Linux, Python, Shell Script, FreeBSD, etc...; • Headbanger :) \m/
  2. 3/52 Sobre esta Apresentação Esta apresentação é uma versão alternativa,

    em português de "On snakes and elephants Using Python inside PostgreSQL" de Jan Urbański https://wulczer.org/pywaw-summit.pdf
  3. 4/52 Sobre Python • Linguagem de altíssimo nível; • Totalmente

    orientada a objetos; • Fácil de aprender e de dar manutenção; • Rica em bibliotecas; • Propósitos gerais; • Tipagem forte e dinânica; • Comunidade forte e solícita; • Objetiva e muito produtiva: faça mais escrevendo menos. import this www.python.org
  4. 5/52 Sobre PostgreSQL É um sistema gerenciador de banco de

    dados objeto-relacional originalmente desenvolvido na Universidade da Califórnia no Departamento de Ciências da Computação em Berkeley. Hoje é desenvolvido e mantido pelo PGDG (PostgreSQL Global Development Group – Grupo de Desenvolvimento Global do PostgreSQL). • Open source; • Rico em recursos avançados; • Muito amigável para programadores; • Extensível www.postgresql.org
  5. 6/52 Stored Procedures no PostgreSQL Em outros SGBDs há diferenças

    entre stored procedures e functions, no PostgreSQL é tudo function; Implementa operações que não seriam fáceis ou sequer seriam possíveis com SQL; Uma função é chamada dentro de SQL; Pode encapsular lógica de negócio;
  6. 7/52 Stored Procedures no PostgreSQL PL/pgSQL é a linguagem padrão

    para se escrever functions no Postgres; PL/Python, PL/Perl e PL/Tcl são nativamente suportadas, mas é possível utilizar outras linguagens procedurais tais como PL/Java, PL/PHP, PL/v8 (JavaScript), PL/R e etc; Suporta sobrecarga de função.
  7. 8/52 Funções: Sintaxe e Exemplos Criação da função fc_foo sem

    parâmetros: > CREATE OR REPLACE FUNCTION fc_foo() RETURNS VARCHAR AS $$ BEGIN RETURN 'Hello, World!'; END; $$ LANGUAGE PLPGSQL;
  8. 9/52 Funções: Sintaxe e Exemplos Criação da função fc_foo com

    parâmetros: > CREATE OR REPLACE FUNCTION fc_foo(num1 INT, num2 INT) RETURNS INT AS $$ BEGIN RETURN (num1 + num2) * 2; END; $$ LANGUAGE PLPGSQL;
  9. 10/52 Funções: Sintaxe e Exemplos Execução da função fc_foo sem

    parâmetros: > SELECT fc_foo(); fc_foo --------------- Hello, World!
  10. 11/52 Funções: Sintaxe e Exemplos Execução da função fc_foo com

    parâmetros: > SELECT fc_foo(2, 5); fc_foo -------- 14
  11. 13/52 Blocos Anônimos > SET client_min_messages = 'notice'; > DO

    $$ DECLARE n_con INT; BEGIN SELECT count(client_addr) INTO n_con FROM pg_stat_activity; RAISE NOTICE 'Conexões não-locais: %', n_con; END; $$ LANGUAGE PLPGSQL; NOTICE: Conexões não-locais: 10
  12. 14/52 PL/Python PL/Python é a implementação de Python como linguagem

    procedural no Postgres; Permite utilizar todas as bibliotecas de Python instaladas no sistema operacional; Ao rodar PL/Python pela primeira vez após o servidor PostgreSQL inicializar, um interpretador Python é inicializado dentro do processo backend; https://www.postgresql.org/docs/current/static/plpython.html
  13. 15/52 PL/Python Para evitar uma demora inicial pode-se carregar a

    biblioteca de sistema plpython.so em alguma configuração “preload”; Tipos de dados PostgreSQL são convertidos para os de Python e vice-versa; Os argumentos de uma função têm escopo global; O módulo plpy fornece acesso ao banco e funções; O “path” dos módulos depende da variável de ambiente PYTHONPATH do usuário que inicia o serviço do PostgreSQL.
  14. 16/52 PL/Python: Tipos de Dados PostgreSQL Python bigint, integer, smallint

    int Real, double float numeric Decimal boolean bool text, varchar, char str array list Tipos personalizados dict null None
  15. 17/52 PL/Python: O “U” da Questão PL/Python é uma linguagem

    procedural considerada “untrusted” (não confiável): o que implica na denominação PL/PYTHONU; O que significa que não é oferecida qualquer forma de restrição para usuários fazer; Permite executar ações fora do banco de dados; Somente superusers podem criar funções em linguagens como PL/PYTHONU. “Com grandes poderes vêm grandes responsabilidades”. Tio Ben (Stan Lee)
  16. 18/52 PL/Python vs PL/pgSQL Qual é a melhor linguagem procedural

    para PostgreSQL? PL/Python ou PL/pgSQL? Depende… De forma geral podemos dizer que para operações puras na base de dados é melhor utilizar PL/pgSQL. E PL/Python para operações que vão além do banco, dando um maior poder de fogo para o PostgreSQL. Cada uma tem seu valor e sua aplicabilidade dentro de um contexto :)
  17. 19/52 PL/Python vs PL/pgSQL PL/pgSQL PL/Python Tipo Descrição TG_OP TD['event']

    string Qual tipo de evento disparou o trigger. Possíveis valores: INSERT, UPDATE, DELETE, ou TRUNCATE. TG_WHEN TD['when'] Quando o gatilho (trigger) foi disparado. Possíveis valores: BEFORE, AFTER, ou INSTEAD OF. TG_LEVEL TD['level'] Qual o nível, por linha ou por comando. Possíveis valores: ROW ou STATEMENT. NEW TD['new'] record Variável que contém a nova linha para INSERT ou UPDATE em nível de linha (ROW level). É nula para DELETE ou nível STATEMENT. OLD TD['old'] Para nível em linha (ROW level), contém a linha antiga para UPDATE ou DELETE. É nula para o vível STATEMENT ou para INSERT. TG_NAME TD['name'] name Nome do gatilho. Variáveis Especiais para Triggers
  18. 20/52 PL/Python vs PL/pgSQL Variáveis Especiais para Triggers PL/pgSQL PL/Python

    Tipo Descrição TG_TABLE_NAME / TG_RELNAME (depreciado) TD['table_name'] name Nome da tabela que disparou o gatilho. TG_TABLE_SCHEMA TD['table_schema'] Nome do esquema (schema) da tabela que disparou o gatilho. TG_RELID TD['relid'] oid OID da tabela que disparou o gatilho. TG_ARGV[] TD['args'] array De string Se o comando CREATE TRIGGER inclui argumentos para a função eles estão disponíveis de TD['args'][0] a TD['args'][n - 1]. Índices de array inválidos (menor do que zero ou maior do que TG_NARGS) resultam em um valor nulo.
  19. 21/52 PL/Python vs PL/pgSQL Variáveis Especiais para Triggers PL/pgSQL PL/Python

    Tipo Descrição TG_NARGS len(TD['args']) inteiro A quantidade de argumentos passados à função do gatilho. Se TD['when'] for BEFORE ou INSTEAD OF e TD['level'] for ROW, você pode retornar None ou “OK” da função Python para indicar que a linha não foi modificada, “SKIP” para abortar o evento de se TD['event'] for INSERT ou UPDATE você pode retornar “MODIFY” para indicar que você modificou nova linha. Caso contrário o valor de retorno será ignorado.
  20. 22/52 PL/Python: Instalação Nosso Laboratório SO: CentOS Linux 7.5 Python:

    3.6 PostgreSQL: 10 Diretório para bibliotecas Python : /var/lib/pgsql/python PYTHONPATH: ${PYTHONPATH}:/var/lib/pgsql/python Obs.: Sinta-se à vontade para usar qualquer versão diferente. Informações meramente ilustrativas que é conforme o laboratório de testes aqui.
  21. 23/52 PL/Python: Instalação Antes da instalação do PostgreSQL indique para

    a variável de ambiente o binário referente à versão de Python utilizada: $ export PYTHON=`which python3.6` É preciso ter também os pacotes com os headers em C de Python (-dev, -devel). No processo de configure utilize a opção --with-python: $ ./configure --with-python
  22. 24/52 PL/Python: Instalação Crie o diretório Python: $ mkdir /var/lib/pgsql/python

    No arquivo de perfil de shell do usuário (.bashrc, .bashrc_profile, .profile) coloque a seguinte linha: export PYTHONPATH="${PYTHONPATH}:/var/lib/pgsql/python"
  23. 25/52 PL/Python: Instalação Para testar podemos iniciar o PostgreSQL passando

    o valor de PYTHONPATH: $ PYTHONPATH="${PYTHONPATH}:/var/lib/pgsql/python" \ pg_ctl start Podemos incluir a variável PYTHONPATH no Unit File SystemD do serviço do PostgreSQL: # systemctl edit --full postgresql.service
  24. 26/52 PL/Python: Instalação Na sessão Unit, diretiva Environment do Unit

    File: [Unit] . . . Environment=PYTHONPATH=/var/lib/pgsql/python Reinicialize o serviço do PostgreSQL: # systemctl restart postgresql.service
  25. 27/52 PL/Python: Exemplos Criação da base de dados de exemplo:

    > CREATE DATABASE db_plpython; Conectar à base: > \c db_plpython Habilitar PL/Python 3 na base de dados atual: > CREATE EXTENSION plpython3u;
  26. 28/52 PL/Python: Exemplos Uma simples função anônima: > DO $$

    from sys import path plpy.notice(path) $$ LANGUAGE PLPYTHON3U; NOTICE: ['/var/lib/pgsql/python', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/ lib64/python3.6/site-packages', '/usr/lib/python3.6/site- packages']
  27. 29/52 PL/Python: Exemplos Função em PL/Python 3 sem argumentos: >

    CREATE OR REPLACE FUNCTION fc_py() RETURNS VARCHAR AS $$ return 'Hello, World!!!' $$ LANGUAGE plpython3u;
  28. 30/52 PL/Python: Exemplos Sobrecarga de função: > CREATE OR REPLACE

    FUNCTION fc_py(num1 INT, num2 INT) RETURNS INT AS $$ return num1 + num2 $$ LANGUAGE plpython3u;
  29. 31/52 PL/Python: Exemplos Testando a primeira função criada: > SELECT

    fc_py(); fc_py ----------------- Hello, World!!! Testando a segunda função criada: > SELECT fc_py(2, 5); fc_py ------- 7
  30. 32/52 PL/Python: Boas Práticas • Mantenha seu código Python em

    um módulo; • Faça todas suas funções SQL com apenas duas linhas; • Teste o código Python fazendo mock [1]. [1] https://docs.python.org/3/library/unittest.mock.html
  31. 33/52 PL/Python: Boas Práticas Criação do módulo teste dentro do

    diretório que está em PYTHONPATH: $ cat << EOF > /var/lib/pgsql/python/teste.py def py_version(): import sys return sys.version.split()[0] EOF Conexão ao banco via psql: $ psql db_plpython
  32. 34/52 PL/Python: Boas Práticas Criação da função seguindo as boas

    práticas: > CREATE OR REPLACE FUNCTION fc_py_version() RETURNS VARCHAR AS $$ from teste import py_version return py_version() $$ LANGUAGE plpython3u;
  33. 35/52 PL/Python: Boas Práticas Teste da função: > SELECT 'Minha

    versão de Python é: '|| fc_py_version() AS "Versão de Python"; Versão de Python --------------------------------- Minha versão de Python é: 3.6.3s
  34. 36/52 PL/Python: Use Case Serão demonstrados 2 (dois) casos de

    uso • Converter XML para JSON, na base de dados de um campo do tipo xml para jsonb; • Em um sistema de cadastro de apostilas PDF extrair o sumário (TOC – Table Of Contents).
  35. 37/52 PL/Python: Use Case Instalar os módulos que serão utilizados:

    # python3 -m pip install xmltodict pdfminer.six Editar o arquivo que será o módulo com funções personalizadas: $ vim /var/lib/pgsql/python/MyFunctions.py
  36. 38/52 PL/Python: Use Case /var/lib/pgsql/python/MyFunctions.py import json import pprint import

    xmltodict from pdfminer.pdfdocument import PDFDocument from pdfminer.pdfparser import PDFParser Continua...
  37. 39/52 PL/Python: Use Case /var/lib/pgsql/python/MyFunctions.py def conv_xml_2_json(doc): 'Converte uma string

    XML para JSON' doc = xmltodict.parse(doc) return json.dumps(doc, ensure_ascii=False) Continua...
  38. 40/52 PL/Python: Use Case /var/lib/pgsql/python/MyFunctions.py def pdf_extract_toc(f): 'Extrai o sumário

    de um PDF' f = open(f, 'rb') parser = PDFParser(f) document = PDFDocument(parser) outlines = document.get_outlines() lista_tmp = [] for (level, title, dest, a, se) in outlines: lista_tmp.append(title) return '\n'.join(lista_tmp)
  39. 41/52 PL/Python: Use Case Converter XML para JSON No banco

    criar a função para converter de XML para JSON: > CREATE OR REPLACE FUNCTION fc_py_xml2jsonb(doc TEXT) RETURNS jsonb AS $$ from MyFunctions import conv_xml_2_json return conv_xml_2_json(doc) $$ LANGUAGE PLPYTHON3U;
  40. 42/52 PL/Python: Use Case Criação da tabela: > CREATE TABLE

    tb_xml( id serial PRIMARY KEY, campo_xml xml);
  41. 43/52 PL/Python: Use Case Inserir o registro: > INSERT INTO

    tb_xml (campo_xml) VALUES ( '<receita nome="pão" tempo_de_preparo="5 minutos" tempo_de_cozimento="1 hora"> <titulo>Pão simples</titulo> <ingredientes> <ingrediente quantidade="3" unidade="xícaras">Farinha</ingrediente> <ingrediente quantidade="7" unidade="gramas">Fermento</ingrediente> <ingrediente quantidade="1.5" unidade="xícaras" estado="morna">Água</ingrediente> <ingrediente quantidade="1" unidade="colheres de chá">Sal</ingrediente> </ingredientes> <instrucoes> <passo>Misture todos os ingredientes, e dissolva bem.</passo> <passo>Cubra com um pano e deixe por uma hora em um local morno.</passo> <passo>Misture novamente, coloque numa bandeja e asse num forno.</passo> </instrucoes> </receita>');
  42. 44/52 PL/Python: Use Case Converter para JSON: > SELECT jsonb_pretty(fc_py_xml2jsonb(campo_xml::text))

    FROM tb_xml; jsonb_pretty ------------------------------------------------------------------------------ { + "receita": { + "@nome": "pão", + "titulo": "Pão simples", + "instrucoes": { + "passo": [ + "Misture todos os ingredientes, e dissolva bem.", + "Cubra com um pano e deixe por uma hora em um local morno.",+ "Misture novamente, coloque numa bandeja e asse num forno." + ] + }, + . . .
  43. 45/52 PL/Python: Use Case Extrair o Sumário de um PDF

    Criação da função: > CREATE OR REPLACE FUNCTION fc_py_pdf_extract_toc (arquivo TEXT) RETURNS TEXT AS $$ from MyFunctions import pdf_extract_toc return pdf_extract_toc(arquivo) $$ LANGUAGE PLPYTHON3U;
  44. 46/52 PL/Python: Use Case Criação da tabela: > CREATE TABLE

    tb_apostila( id serial PRIMARY KEY, titulo VARCHAR(200), autor VARCHAR(70), toc text);
  45. 47/52 PL/Python: Use Case Inserir um registro: > INSERT INTO

    tb_apostila (titulo, autor, toc) VALUES ( 'PostgreSQL - SQL Básico', 'Juliano Atanazio', fc_py_pdf_extract_toc('/tmp/postgresql_sql_basico.pdf'));
  46. 48/52 PL/Python: Use Case Verificar o que foi inserido: >

    SELECT id, titulo, autor, toc FROM tb_apostila; id | titulo | autor | toc ----+-------------------------+------------------+------------------------------------------------------------- 1 | PostgreSQL - SQL Básico | Juliano Atanazio | 1 SQL + | | | 1.1 O que é SQL + | | | 1.2 Subdivisões SQL + | | | 1.2.1 DDL + | | | 1.2.2 DML + | | | 1.2.3 DCL + | | | 1.3 Identificadores + | | | 1.4 Operadores + | | | 1.5 Comentários SQL + | | | 2 Tipos de Dados + | | | 2.1 Sobre Tipos de Dados + . . .
  47. 49/52 Conclusão Utilize PL/Python para coisas que PL/pgSQL não atende.

    PL/Python por ser a implementação de Python como linguagem procedural no PostgreSQL permite que se use todo seu poder das “baterias inclusas” além de pacotes de bibliotecas extras instaladas via pip, por exemplo.