Download - Falando "Postgrês"

Transcript
Page 1: Falando "Postgrês"

FALANDO "POSTGRÊS"DICKSON S. GUEDES

22° PYTHONFLORIPA MEETUPhttps://youtu.be/tB0uNqjwbX8?t=7h14m57s

Page 2: Falando "Postgrês"

BEGIN TRANSACTION;Por que estamos aqui hoje?

Page 3: Falando "Postgrês"

AVISO IMPORTANTEEsta palestra conterá cenas fortes de assassinato às boas

práticas de códigos.

Também conterá códigos que provocarão os mais diferentesestímulos musculares em sua face. `(leia-se "sua face" mesmo,

e não "seu feice")

Existe o risco de que alguns paradigmas seus sejamquebrados.

Você pode querer começar a se retirar… tudo bem…

Page 4: Falando "Postgrês"

EMACS ROCKS!Esta palestra é totalmente escrita e apresentada utilizando oeditor 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…

Page 5: Falando "Postgrês"

SEM MEMES?

Page 6: Falando "Postgrês"

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 oPython e o Elefante?

Page 7: Falando "Postgrês"

LICENÇA

Page 8: Falando "Postgrês"

PYTHONGPL Compatível

Page 9: Falando "Postgrês"

POSTGRESQLPostgreSQL License

Page 10: Falando "Postgrês"

PADRÕES

Page 11: Falando "Postgrês"

PEP1-8: Guidelines, Code Style, …20: The Zen of Python248: Database API Spec v1.0249: Database API Spec v2.0257: Docstring

Page 12: Falando "Postgrês"

SQL (ISO/IEC)92: CLI, PSM, DATETIME, UNION, …,INFORMATION_SCHEMA, CAST, …1999: MED, OLB, JRT, GROUP BY, ROLLUP, WITHRECURSIVE2003: XML, Window Functions, SEQUENCES, MERGE*2006: XML, XQuery2008: TRUNCATE, Partitioned JOIN2011: Temporal2016: JSON, Row Pattern Matching

Page 13: Falando "Postgrês"

DOCUMENTAÇÃOhttps://www.postgresql.org/docs/https://www.postgresql.org/docs/current/static/index.htmlhttps://www.postgresql.org/docs/9.6/static/index.htmlhttps://www.postgresql.org/docs/9.5/static/index.html

Page 14: Falando "Postgrês"

OOrespirem …

Page 15: Falando "Postgrês"

EXTENSIBILIDADE

Page 16: Falando "Postgrês"

ESTENDENDO PYTHONPython

import spamstatus = spam.system("ls -l")

C#include <Python.h>

static PyObject *spam_system(PyObject *self, PyObject *args){ const char *command; int sts;

if (!PyArg_ParseTuple(args, "s", &command)) return NULL; sts = system(command); return Py_BuildValue("i", sts);}

Page 17: Falando "Postgrês"

ESTENDENDO POSTGRESQL

Page 18: Falando "Postgrês"

VIA CATÁLOGO# \dt pg_catalog. List of relations Schema | Name | Type | Owner ------------+-------------------------+-------+---------- pg_catalog | pg_aggregate | table | postgres ... pg_catalog | pg_class | table | postgres ... pg_catalog | pg_database | table | postgres pg_catalog | pg_foreign_data_wrapper | table | postgres pg_catalog | pg_foreign_server | table | postgres pg_catalog | pg_foreign_table | table | postgres pg_catalog | pg_index | table | postgres ... pg_catalog | pg_language | table | postgres ... pg_catalog | pg_operator | table | postgres ... pg_catalog | pg_type | table | postgres pg_catalog | pg_user_mapping | table | postgres

Page 19: Falando "Postgrês"

FUNÇÕES, OPERADORES, TIPOS E DOMÍNIOS

Criando um dominio de dados para CPF e um operadoruná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 SQLCOST 10IMMUTABLE STRICTAS $_$ 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

Page 20: Falando "Postgrês"

select row_number() over () as posicao, valor::int 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);

Page 21: Falando "Postgrês"

);

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

Page 22: Falando "Postgrês"

VIA C#include "postgres.h"#include <string.h>#include "fmgr.h"#include "utils/geo_decls.h"

#ifdef PG_MODULE_MAGICPG_MODULE_MAGIC;#endif

PG_FUNCTION_INFO_V1(makepoint);Datummakepoint(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 meubanco.

CREATE FUNCTION makepoint(point, point) RETURNS point AS 'DIRECTORY/funcs', 'makepoint' LANGUAGE C STRICT;

Page 23: Falando "Postgrês"

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

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]]);

Page 24: Falando "Postgrês"

ERRO: não pode converter matrizmultidimensional para lista python detalhe:

pl/python só suporta matrizesunidimensionais. contexto: função pl/python

"array_transpose"/

Page 25: Falando "Postgrês"

REPOSITÓRIO DE EXTENSÕES

Page 26: Falando "Postgrês"

PYPI

pip search …pip install ……

Page 27: Falando "Postgrês"

PGXN CLIENT

pgxn search …pgxn install ……

Page 28: Falando "Postgrês"

INTEROPERABILIDADE

Page 29: Falando "Postgrês"

LIBPQasync non-blockstream (controle de �uxo)text/binarynoti�cações assíncronas (LISTEN/NOTIFY)COPY (trasferência de dados)

Page 30: Falando "Postgrês"

PSYCOPG2Exemplo básico de execução:

import psycopg2

dbconn = psycopg2.connect(host="/var/run/postgresql", dbname="guedes")cursor = dbconn.cursor()

cursor.execute("""SELECT relnameFROM pg_classWHERE relkind='r'""")

for row in cursor.fetchall(): print("Tabela: {}".format(row[0]))

cursor.close()dbconn.close()

Page 31: Falando "Postgrês"

implementado em C, como wrapper da libpqnoti�caçõesCOPY…

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)');

Page 32: Falando "Postgrês"

CURIOSIDADEA biblioteca psycopg2 e o cliente de linha de comando pgxn

são de autoria de Daniele Varrazzo

Page 33: Falando "Postgrês"

:|respirem …

Page 34: Falando "Postgrês"

"O POSTGRÊS"Costumamos falar em "um código Pythônico"… mas e com

SQL?

Page 35: Falando "Postgrês"

O ANINHADOR FRENÉTICO

Page 36: Falando "Postgrês"

PROBLEMASELECT ..., CASE WHEN sobrenome IS NULL THEN nome WHEN sobrenome IS NOT NULL THEN sobrenome || ',' || nome ENDFROM ...

Page 37: Falando "Postgrês"

SOLUÇÃODai você vai lá e mostra como faz…

SELECT ..., COALESCE(sobrenome || ',', '') || nomeFROM ...

Page 38: Falando "Postgrês"

O ANINHADOR FRENÉTICO II

Page 39: Falando "Postgrês"

PROBLEMASELECT ..., CASE WHEN COALESCE(endereco, '') <> '' THEN CASE WHEN COALESCE( COALESCE(endereco, '') || ' ' || COALESCE(bairro, '') ) <> ' ' THEN endereco ||' '|| bairro ELSE COALESCE(cidade, '') END ELSE COALESCE(cidade,'SEM CIDADE') ENDFROM ...LEFT JOIN ...LEFT JOIN ...

Page 40: Falando "Postgrês"

POSSÍVEL SOLUÇÃOSELECT ..., COALESCE(endereco ||' '|| bairro, cidade, 'SEM CIDADE'),FROM ...LEFT JOIN ...LEFT JOIN ...

Page 41: Falando "Postgrês"

OPA! TRÊS PARÂMETROS?SELECT ..., COALESCE(endereco ||' '|| bairro, cidade, 'SEM CIDADE'), ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^FROM ...LEFT JOIN ...LEFT JOIN ...

Page 42: Falando "Postgrês"

QUAL É O RESULTADO DISTO?SELECT ..., COALESCE(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, 'SEM VALOR'), COALESCE(NULL, 'SEM VALOR'), ...FROM ...

Page 43: Falando "Postgrês"

COMO ISSO É POSSÍVEL?

Page 44: Falando "Postgrês"

???

Page 45: Falando "Postgrês"

???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

Page 46: Falando "Postgrês"

CONHEÇA BEM AS FERRAMENTAS QUE VOCÊ USA

Page 47: Falando "Postgrês"

O POSTGRES TEM UM TYPO JSON E UM TIPO JSONB, SABIA?E suporte índices!

{ "nome": "Joao", "endereco" : { "cep": "88000-00", "rua": "Das Saudades", "nro": 453, "bairro": "Centro" }, "dependentes": [ { "nome": "Maria", "parentesco": "esposa" }, { "nome": "Mario", "parentesco": "filho" }, { "nome": "Marina", "parentesco": "filha" } ]}

Page 48: Falando "Postgrês"

AS FUNÇÕES DE AGREGAÇÃO DO POSTGRES SÃO COMO MAP-REDUCE

https://www.postgresql.org/docs/current/static/xaggr.html

Page 49: Falando "Postgrês"

AS VEZES TEM FUNÇÕES QUE VOCÊ NEM IMAGINASELECT make_date(2017, 7, 7);

Page 50: Falando "Postgrês"

O EXPLORADOR DO DESCONHECIDO

Page 51: Falando "Postgrês"

NONE VS NULLQual é o resultado desta expressão em Python?

print(None + 1)

E qual é o resultado desta expressão no PostgreSQL?SELECT NULL + 1 as resultado;

Page 52: Falando "Postgrês"

NULL E A ARITIMÉTICA\tSELECT NULL = NULL;SELECT NULL > NULL;SELECT NULL < NULL;

Page 53: Falando "Postgrês"

O QUE É NULL?\tSELECT NULL IS NULL; => trueSELECT NULL IS DISTINCT FROM NULL; => falseSELECT NULL IS NOT DISTINCT FROM NULL; => true

SELECT 1 IS NULL; => falseSELECT 1 IS DISTINCT FROM NULL; => trueSELECT 1 IS NOT DISTINCT FROM NULL; => false

Page 54: Falando "Postgrês"

TESTE DE ATENÇÃO!

Page 55: Falando "Postgrês"

LEMBRAM DA FUNÇÃO MENOR_DE_TODOS?SELECT menor_de_todos(10, 11, 12, 30, -20, -30) as menor;

Page 56: Falando "Postgrês"

E SE EU PASSAR NULL?SELECT menor_de_todos(10, 11, 12, NULL, -20, -30) as menor;

Page 57: Falando "Postgrês"

EXPLICAÇÃOmenor_de_todos(...) usa a função min(...) que éuma função de agregação e funções de agregação ignoram

NULL.

Page 58: Falando "Postgrês"

O FABRICADOR DE REGISTROS

Page 59: Falando "Postgrês"

UM REGISTRO TAMBÉM É CHAMADO DE TUPLA!SELECT ROW(10, 'JOAO', 1500.50);

Page 60: Falando "Postgrês"

DOIS REGISTROS SENDO COMPARADOS!SELECT ROW(10, 'JOAO', 1500.50) = ROW(10, 'JOAO', 1500.50); => true

SELECT ROW(10, 'JOAO', 1500.50) > ROW(9, 'PEDRO', 500.50); => true

SELECT ROW(1, 'MARIA', 1000.10) > ROW(1, 'MARIA', 1000.11); => false

SELECT ROW(100, 'JOANA', 10.60) < ROW(100, 'JOANA', NULL); => ?????

Page 61: Falando "Postgrês"

NULL!Sim, o resultado é NULL, porque o resultado é desconhecido.

Page 62: Falando "Postgrês"

:/respirem …

Page 63: Falando "Postgrês"

LEMBRA O INSERT?INSERT INTO ... VALUES (1, 'JOAO', 1500.10);

Page 64: Falando "Postgrês"

PENSA NO VALUES!/* INSERT INTO ... */ VALUES (1, 'JOAO', 1500.10); -- FUNCIONA!

Page 65: Falando "Postgrês"

VALUES É UM COMANDO SOZINHO!INSERT INTO ... SELECT codigo, nome, valor FROM tabela_temporaria;

INSERT INTO ... VALUES (1, 'JOAO', 1500.10);

VALUES (1, '...', 1500.10), (2, '...', 500.00), (3, '...', 100.00), (4, '...', 50.50), (5, '...', 3500.00);

SELECT * FROM (VALUES (1, '...', 1500.10), (2, '...', 500.00), (3, '...', 100.00), (4, '...', 50.50), (5, '...', 3500.00)) AS foo WHERE column3 > 100; -- quais registros aparecem?

Page 66: Falando "Postgrês"

PARA O INFINITO, E ALÉM

Page 67: Falando "Postgrês"

SIM, TUDO ISSO É POSSÍVELSELECT '+Infinity'::float > 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; => true

SELECT '-Infinity'::float < 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; => true

Page 68: Falando "Postgrês"

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'

Page 69: Falando "Postgrês"

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'

Page 70: Falando "Postgrês"

NANANANANANANANSELECT 'NaN'::numeric + 1; => 'NaN'

Page 71: Falando "Postgrês"

LATERAL JOINSELECT conta, ultimo_movimento.valorFROM movimento mLATERAL (SELECT valor FROM movimento _m WHERE _m.conta = m.conta AND _m.data < m.data ORDER BY data DESC LIMIT 1) as ultimo_movimentoWHERE m.data = current_date AND m.conta = 1214124;

Page 72: Falando "Postgrês"

VAMOS VOLTAR UM POUCO PARA O PYTHON?antes… só mais uma coisinha ….

Page 73: Falando "Postgrês"

:)respirem …

Page 74: Falando "Postgrês"

FDW - FOREIGN DATA WRAPPERS�le_fdwpostgres_fdwmysql_fdwmongo_fdwmulticorn

Page 75: Falando "Postgrês"

FDW - UM EXEMPLO NATIVOExemplo 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_USERSERVER 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 FROM recursos_humanos.tb_funcionarios

Page 76: Falando "Postgrês"

FROM recursos_humanos.tb_funcionarios WHERE ((cpf = 1234567891::numeric)) Planning time: 0.061 ms

Execution time: 105.232 ms(5 registros)

Page 77: Falando "Postgrês"

FDW - UM EXEMPLO MULTICORNSHOW ME THE CODE!!

–> Telegram FDW

Page 78: Falando "Postgrês"

CONCLUSÕESSaiba o quanto você sabeSaiba o quanto você ainda não sabeSaiba que jamais saberás tudo mas seja curiosoLeia e se questione: "Será que …?"Ensine o que aprendeu e …

Page 79: Falando "Postgrês"

:Dinspirem …