Qual é a melhor maneira de armazenar um endereço de email no PostgreSQL?

40

Qual seria o tipo de dados correto para armazenar endereços de email no PostgreSQL?

Posso usar varchar(ou até mesmo text), mas me pergunto se existe um tipo de dados mais específico para emails.

Adam Matan
fonte

Respostas:

38

DOMAINS personalizados

Eu não acho que usar citext(sem distinção entre maiúsculas e minúsculas) seja suficiente [1] . Usando o PostgreSQL, podemos criar um domínio personalizado que é essencialmente algumas restrições definidas sobre um tipo . Podemos criar um domínio, por exemplo, sobre o citexttipo ou sobre text.

Usando type=emailespecificação HTML5

Atualmente, a resposta mais correta para a pergunta "qual é um endereço de email" está especificada no RFC5322 . Essa especificação é incrivelmente complexa [2] , tanto que tudo a quebra. O HTML5 contém uma especificação diferente para email ,

Esse requisito é uma violação intencional do RFC 5322, que define uma sintaxe para endereços de email que são simultaneamente muito rígidos (antes do caractere "@"), muito vagos (após o caractere "@") e muito relaxados (permitindo comentários) , caracteres de espaço em branco e seqüências de caracteres citadas de maneiras não familiares à maioria dos usuários) para serem úteis aqui. [...] A seguinte expressão regular compatível com JavaScript e Perl é uma implementação da definição acima.

/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/

Provavelmente, é isso que você deseja e, se for bom o suficiente para HTML5, provavelmente será bom o suficiente para você. Podemos fazer isso diretamente no PostgreSQL. Também uso citextaqui (o que tecnicamente significa que você pode simplesmente regex um pouco visualmente removendo as maiúsculas ou minúsculas).

CREATE EXTENSION citext;
CREATE DOMAIN email AS citext
  CHECK ( value ~ '^[a-zA-Z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$' );

Agora você pode fazer ...

SELECT '[email protected]'::email;

Mas não

SELECT 'asdf@foob,,ar.com'::email;
SELECT 'asd@[email protected]'::email;

Porque ambos retornam

ERROR:  value for domain email violates check constraint "email_check"

Porque isso também é baseado no citext

SELECT '[email protected]'::email = '[email protected]';

retorna true por padrão.

Usando plperlu/Email::Valid

Como uma observação importante, existe um método mais correto de fazer isso, que é muito mais complexo usando plperlu. Se você precisa desse nível de correção, não deseja citext. Email::Validpode até verificar se o domínio possui um registro MX (exemplo nos documentos de Email :: Valid)! Primeiro, adicione plperlu (requer superusuário).

CREATE EXTENSION plperlu;

Em seguida, crie a função , observe que marcamos como IMMUTABLE:

CREATE FUNCTION valid_email(text)
  RETURNS boolean
  LANGUAGE plperlu
  IMMUTABLE LEAKPROOF STRICT AS
$$
  use Email::Valid;
  my $email = shift;
  Email::Valid->address($email) or die "Invalid email address: $email\n";
  return 'true';
$$;

Em seguida, crie o domínio ,

CREATE DOMAIN validemail AS text NOT NULL
  CONSTRAINT validemail_check CHECK (valid_email(VALUE));

Notas de rodapé

  1. O uso citexté tecnicamente errado. O SMTP define local-partcomo diferencia maiúsculas de minúsculas. Mas, novamente, este é um caso de especificação sendo estúpida. Ele contém suas próprias crises de identidade. A especificação diz local-part(a parte anterior a @) "PODE ser sensível a maiúsculas e minúsculas" ... "DEVE ser tratada como maiúscula e minúscula" ... e ainda "explorar a sensibilidade de maiúsculas e minúsculas das peças locais da caixa de correio impede a interoperabilidade e é desencorajada".
  2. A especificação de um endereço de e-mail é tão complexa que nem é independente. Complexo é realmente um eufemismo, aqueles que fazem as especificações nem o entendem. . Dos documentos sobre regular-expression.info

    Nenhuma dessas expressões regulares impõe limites de tamanho no endereço de email geral ou na parte local ou nos nomes de domínio. O RFC 5322 não especifica nenhuma limitação de comprimento. Isso decorre de limitações em outros protocolos, como o protocolo SMTP, para realmente enviar email. O RFC 1035 afirma que os domínios devem ter 63 caracteres ou menos, mas não o incluem em sua especificação de sintaxe. O motivo é que um idioma comum verdadeiro não pode impor um limite de comprimento e não permitir hífens consecutivos ao mesmo tempo.

Evan Carroll
fonte
1
O link do W3.org está quebrado; aqui está uma fonte alternativa: html.spec.whatwg.org/multipage/…
MaxGabriel
@ MaxGabriel, obrigado, fique por aqui, você receberá as permissões de edição em breve. Vou consertá-la lá.
Evan Carroll
Existe uma razão para ter ambas a-ze A-Znas classes de personagens?
xehpuk 31/05
@xehpuk bem, porque ~é sensível a maiúsculas e minúsculas, você precisa (a) usar ~*maiúsculas e minúsculas ou (b) ter as letras maiúsculas e minúsculas na classe char.
Evan Carroll
citext's ~parece ser case-insensitive para mim, é por isso que eu estou pedindo.
xehpuk
46

Eu sempre uso CITEXTpara email, porque um endereço de email não diferencia maiúsculas de minúsculas , ou seja, [email protected] é o mesmo que [email protected].

Também é mais fácil configurar um índice exclusivo para evitar duplicatas, em comparação com o texto:

-- citext
CREATE TABLE address (
   id serial primary key,
   email citext UNIQUE,
   other_stuff json
);

-- text
CREATE TABLE address (
   id serial primary key,
   email text,
   other_stuff json
);
CREATE UNIQUE INDEX ON address ((lower(email)));

Comparar e-mails também é mais fácil e menos propenso a erros:

SELECT * FROM address WHERE email = '[email protected]';

em comparação com:

SELECT * FROM address WHERE lower(email) = lower('[email protected]');

CITEXTé um tipo definido em um módulo de extensão padrão chamado "citext" e disponível digitando:

CREATE EXTENSION citext;

PS texte varcharsão praticamente os mesmos no Postgres e não há penalidade por usar textcomo se pode esperar. Verifique esta resposta: Diferença entre texto e varchar

hegemon
fonte
10

Eu sempre uso varchar(254)como endereço de email não pode ter mais de 254 caracteres.

Consulte https://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address

O Postgresql não possui um tipo interno para endereços de email, embora eu tenha encontrado alguns tipos de dados contribuídos.

Além disso, você pode adicionar um gatilho ou uma lógica desse tipo para padronizar os endereços de email, caso deseje adicionar uma chave exclusiva.

Em particular, a domainparte do endereço de email (que é do formato local-part@ não domainfaz distinção entre maiúsculas e minúsculas e local-partdeve ser tratada como diferencia maiúsculas de minúsculas. Consulte http://tools.ietf.org/html/rfc5321#section-2.4

Outra consideração é se você deseja armazenar nomes e endereços de email no formulário "Joe Bloggs" <[email protected]>. Nesse caso, você precisa de uma sequência com mais de 254 caracteres e não poderá usar significativamente uma restrição exclusiva. Eu não faria isso e sugiro que armazene o nome e o endereço de email separadamente. Endereços de impressão bonitos neste formato sempre são possíveis em sua camada de apresentação.

Colin 't Hart
fonte
De acordo com 4.5.3.1. Limites e mínimos de tamanho , o comprimento máximo é de 320 caracteres (incluindo o @).
Andriy M
1
@AndriyM Não há nada na seção referenciada que diz 320. E isso está errado de qualquer maneira; tools.ietf.org/html/rfc5321#section-4.5.3.1.3 afirma que o comprimento máximo de um caminho é de 256 caracteres e que deve incluir os "<" e ">" circundantes, com o máximo de 254.
Colin 't Hart
Cheguei a 320 no máximo com base em 4.5.3.1.1 ("O comprimento total máximo de um nome de usuário ou de outra parte local é de 64 octetos") e 4.5.3.1.2 ("O comprimento total máximo de um nome de domínio ou o número é 255 octetos "). Então, 64 + 255 + 1 (o @) = 320. Talvez eu esteja interpretando errado.
Andriy M
3
@AndriyM Leia a resposta aceita para a pergunta à qual vinculei. Isso explica tudo. É definitivamente 254, e não 320.
Colin 't Hart
3

Você pode estar interessado em usar uma verificação CONSTRAINT (possivelmente mais fácil, mas pode rejeitar mais do que gostaria ou usar uma FUNÇÃO discutida aqui e aqui . Basicamente, trata-se de compensações entre especificidade e facilidade de implementação. embora. PostgreSQL ainda tem um tipo de endereço IP nativa, mas há um projeto em pgFoundry para um tipo de dados de e-mail aqui . no entanto, o melhor que eu encontrei sobre este é um e-mail de domínio. O domínio é melhor do que uma restrição de verificação, porque, se você o alterar, é necessário fazê-lo apenas uma vez na definição de domínio e não seguir as trilhas das tabelas pai-filho, alterando todas as restrições de verificação. Os domínios são realmente legais - como tipos de dados, mas mais simples de implementar. Eu os usei no Firebird - a Oracle nem os possui!

Vérace
fonte