1) A palestra introduz o PostgreSQL e discute como ele se relaciona com o Python, incluindo padrões, documentação, extensibilidade e interoperabilidade entre as duas tecnologias.
2) O documento também discute conceitos importantes do PostgreSQL como tipos JSON, funções de agregação, NULL vs None e registros/tuplas.
3) Finalmente, o documento explora vários recursos avançados do SQL como VALUES, operações com NULL e infinito.
3. AVISO IMPORTANTE
Esta palestra conterá cenas fortes de assassinato às boas
práticas de códigos.
Também conterá códigos que provocarão os mais diferentes
estímulos musculares em sua face. `(leia-se "sua face" mesmo,
e não "seu feice")
Existe o risco de que alguns paradigmas seus sejam
quebrados.
Você pode querer começar a se retirar… tudo bem…
4. EMACS ROCKS!
Esta palestra é totalmente escrita e apresentada utilizando o
editor de texto Emacs com org-mode `(leia-se "não vai ter
memes")
e este talvez seja o primeiro paradigma a ser quebrado…
Além de uma tela preta e apresentação em texto você verá
códigos em Python e em SQL sendo reproduzidos aqui dentro
mesmo…
PS: ainda dá tempo de sair…
6. VAMOS ENTÃO COMEÇAR?
A language that doesn't affect the way you think about
programming, is not worth knowing. - Alan Perlis
Então vamos conhecer um pouco mais das relações entre o
Python e o Elefante?
19. FUNÇÕES, OPERADORES, TIPOS E DOMÍNIOS
Criando um dominio de dados para CPF e um operador
unário que o valida:
BEGIN;
DROP SCHEMA IF EXISTS teste CASCADE;
CREATE SCHEMA teste;
SET search_path TO teste;
CREATE OR REPLACE FUNCTION cpf_valido(numeric)
RETURNS BOOLEAN
LANGUAGE SQL
COST 10
IMMUTABLE STRICT
AS
$_$
with
cpf as (
select $1 as numero
),
cpf_formatado as (
select lpad(cpf.numero::text,11,'0') as numero
from cpf
),
matriz as (
select regexp_split_to_table( cpf_formatado.numero, E's*' ) as valor
from cpf_formatado
),
digitos_por_posicao_1 as (
select row_number() over () as posicao, valor::int
20. from matriz
),
digitos_por_posicao_2 as (
select posicao - 1 as posicao, valor
from digitos_por_posicao_1
),
digito_1 as (
select sum(posicao*valor) as soma,
sum(posicao*valor) % 11 as resto
from digitos_por_posicao_1 where posicao<=9
),
digito_2 as (
select sum(posicao*valor) as soma,
sum(posicao*valor) % 11 as resto
from digitos_por_posicao_2 where posicao<=9
),
cpf_esperado as (
select array_to_string(array_agg(valor),'')::numeric as numero
from
(
select valor from digitos_por_posicao_1 where posicao <=9
union all
select resto from digito_1
union all
select resto from digito_2
) as foo
)
select distinct cpf.numero = cpf_esperado.numero
from cpf, cpf_esperado;
$_$;
CREATE OPERATOR #? (
LEFTARG = numeric,
PROCEDURE = cpf_valido
21. );
CREATE DOMAIN cpf AS numeric CHECK ( cpf_valido(VALUE) );
COMMIT;
E agora testar para ver como funciona…
SET search_path TO teste;
DROP TABLE IF EXISTS teste.pessoa;
SELECT cpf_valido(59328253241);
SELECT 59328253241 #? AS cpf_valido;
SELECT 37821042773 #? AS cpf_valido;
SELECT 91416742433 #? AS cpf_valido;
SELECT 91416000433 #? AS cpf_valido;
SELECT 37821042003 #? AS cpf_valido;
SELECT NOT 37821042003 #? AS cpf_valido;
SELECT NOT 91416000433 #? AS cpf_valido;
CREATE TABLE teste.pessoa (
nro_cpf cpf
);
INSERT INTO teste.pessoa VALUES(88229346798);
INSERT INTO teste.pessoa VALUES(45476684425);
E se eu tentar inserir CPF INVALIDO!?
INSERT INTO pessoa VALUES(45076684425);
INSERT INTO pessoa VALUES(81249396798);
22. VIA C
#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "utils/geo_decls.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
PG_FUNCTION_INFO_V1(makepoint);
Datum
makepoint(PG_FUNCTION_ARGS)
{
/* Here, the pass-by-reference nature of Point is not hidden. */
Point *pointx = PG_GETARG_POINT_P(0);
Point *pointy = PG_GETARG_POINT_P(1);
Point *new_point = (Point *) palloc(sizeof(Point));
new_point->x = pointx->x;
new_point->y = pointy->y;
PG_RETURN_POINT_P(new_point);
}
E para o SQL reconhecer esta função, preciso criá-la em meu
banco.
CREATE FUNCTION makepoint(point, point) RETURNS point
AS 'DIRECTORY/funcs', 'makepoint'
LANGUAGE C STRICT;
23. VIA LINGUAGEM_DO_SEU_CORACAO
Criar a linguagem utilizando extensão:
create extension if not exists plpython2u;
create or replace function array_transpose(a float[])
returns float[]
language plpython2u
as $$
import numpy as np
return np.array(a).transpose()
$$;
E usar ela no SQL:
select array_transpose(array[1.0, 2.0, 4.5]);
Mas com algumas limitações, as vezes:
select array_transpose(array[ array[1.0, 2.0], array[4.5, 7.8]]);
24. ERRO: não pode converter matriz
multidimensional para lista python detalhe:
pl/python só suporta matrizes
unidimensionais. contexto: função pl/python
"array_transpose"/
30. PSYCOPG2
Exemplo básico de execução:
import psycopg2
dbconn = psycopg2.connect(host="/var/run/postgresql", dbname="guedes")
cursor = dbconn.cursor()
cursor.execute("""
SELECT relname
FROM pg_class
WHERE relkind='r'
""")
for row in cursor.fetchall():
print("Tabela: {}".format(row[0]))
cursor.close()
dbconn.close()
31. implementado em C, como wrapper da libpq
noti cações
COPY
…
criação de tipo personalizados
equivalencia de tipos Python vs PostgreSQL
from psycopg2.extensions import adapt, register_adapter, AsIs
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def adapt_point(point):
x = adapt(point.x).getquoted()
y = adapt(point.y).getquoted()
return AsIs("'(%s, %s)'" % (x, y))
register_adapter(Point, adapt_point)
cur.execute("INSERT INTO atable (apoint) VALUES (%s)",
(Point(1.23, 4.56),))
INSERT INTO atable (apoint) VALUES ('(1.23, 4.56)');
32. Um outro exemplo é o tipo
CURIOSIDADE
A biblioteca psycopg2 e o cliente de linha de comando pgxn
são de autoria de Daniele Varrazzo
39. PROBLEMA
SELECT ...,
CASE WHEN COALESCE(endereco, '') <> ''
THEN CASE WHEN COALESCE(
COALESCE(endereco, '') ||
' ' ||
COALESCE(bairro, '')
) <> ' '
THEN endereco ||' '|| bairro
ELSE COALESCE(cidade, '')
END
ELSE COALESCE(cidade,'SEM CIDADE')
END
FROM ...
LEFT JOIN ...
LEFT JOIN ...
45. ???
VARIADIC + SYNTAX SUGAR
CREATE OR REPLACE FUNCTION menor_de_todos(VARIADIC valores numeric[])
RETURNS numeric AS
$$
SELECT min($1[valor])
FROM generate_subscripts($1, 1) as g(valor);
$$
LANGUAGE SQL;
SELECT menor_de_todos(10, 11, 12, 30, -20, -30) as menor;
/*
menor
--------
-30
(1 row)
*/
Parâmetros! Não é para passar um ARRAY não!
SELECT menor_de_todos(ARRAY[10, 11, 12, 30, -20, -30]) as menor;
^^^^^^
`-- assim da ruim
51. NONE VS NULL
Qual é o resultado desta expressão em Python?
print(None + 1)
E qual é o resultado desta expressão no PostgreSQL?
SELECT NULL + 1 as resultado;
52. NULL E A ARITIMÉTICA
t
SELECT NULL = NULL;
SELECT NULL > NULL;
SELECT NULL < NULL;
53. O QUE É NULL?
t
SELECT NULL IS NULL; => true
SELECT NULL IS DISTINCT FROM NULL; => false
SELECT NULL IS NOT DISTINCT FROM NULL; => true
SELECT 1 IS NULL; => false
SELECT 1 IS DISTINCT FROM NULL; => true
SELECT 1 IS NOT DISTINCT FROM NULL; => false
68. INFINITO PARA DATAS, TAMBÉM, CLARO!
SELECT 'Infinity'::date > current_date; => true
INSERT INTO elemento(nome, validade) VALUES ('uranio', 'Infinity');
isto é um date _.^^^^^^^^
SELECT 'today'::interval = current_date; => true
SELECT 'yesterday'::date = 'today'::date - interval '1 day'; => true
SELECT 'tomorroy'::date = 'today'::date + interval '1 day'; => true
SELECT current_date + 'allballs'::time; => '2015-09-18 00:00:00'
69. E TUDO PODE SER RESCRITO COMO …
SELECT date 'Infinity' > current_date; => true
INSERT INTO elemento(nome, validade) VALUES ('uranio', 'Infinity');
isto é um date _.^^^^^^^^
SELECT interval 'today' = current_date; => true
SELECT date 'yesterday' = date 'today' - interval '1 day'; => true
SELECT date 'tomorroy' = date 'today' + interval '1 day'; => true
SELECT current_date + time 'allballs'; => '2015-09-18 00:00:00'
71. LATERAL JOIN
SELECT conta, ultimo_movimento.valor
FROM movimento m
LATERAL (SELECT valor
FROM movimento _m
WHERE _m.conta = m.conta
AND _m.data < m.data
ORDER BY data DESC
LIMIT 1) as ultimo_movimento
WHERE m.data = current_date
AND m.conta = 1214124;
72. VAMOS VOLTAR UM POUCO PARA O PYTHON?
antes… só mais uma coisinha ….
75. FDW - UM EXEMPLO NATIVO
Exemplo do postgres_fdw, ou seja, um Postgres
conversando com outro…
CREATE EXTENSION postgres_fdw;
CREATE SERVER servidor_de_consultas
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host '10.100.1.1', dbname 'filial_sul', port '5432');
CREATE USER MAPPING FOR CURRENT_USER
SERVER servidor_de_consultas
OPTIONS (user 'consulta', password 'consulta');
CREATE FOREIGN TABLE pessoas (cpf numeric, nome varchar)
SERVER servidor_de_consultas
OPTIONS ( schema_name 'recursos_humanos', table_name 'tb_funcionarios');
EXPLAIN (ANALYZE,VERBOSE, BUFFERS)
SELECT * FROM pessoas
WHERE cpf = 1234567891;
QUERY PLAN
-------------------------------------------------------------------------------
Foreign Scan on public.pessoas (cost=100.00..118.06 rows=3 width=104)
Output: cpf, nome
Remote SQL: SELECT cpf, nome
77. FDW - UM EXEMPLO MULTICORN
SHOW ME THE CODE!!
–> Telegram FDW
78. CONCLUSÕES
Saiba o quanto você sabe
Saiba o quanto você ainda não sabe
Saiba que jamais saberás tudo mas seja curioso
Leia e se questione: "Será que …?"
Ensine o que aprendeu e …