Qual è il modo migliore per memorizzare un indirizzo e-mail in PostgreSQL?


40

Quale sarebbe il giusto tipo di dati per memorizzare gli indirizzi e-mail in PostgreSQL?

Posso usare varchar(o anche text), ma mi chiedo se esiste un tipo di dati più specifico per le e-mail.

Risposte:


38

DOMAINS personalizzato

Non credo che usare citext(senza distinzione tra maiuscole e minuscole) sia sufficiente [1] . Usando PostgreSQL possiamo creare un dominio personalizzato che è essenzialmente alcuni vincoli definiti su un tipo . Possiamo creare un dominio, ad esempio, sul citexttipo o su text.

Usando le type=emailspecifiche HTML5

Attualmente la risposta più corretta alla domanda che cos'è un indirizzo e-mail è specificata in RFC5322 . Quella specifica è follemente complessa [2] , tanto che tutto la rompe. HTML5 contiene una specifica diversa per l'email ,

Questo requisito è una violazione intenzionale di RFC 5322, che definisce una sintassi per gli indirizzi di posta elettronica che è contemporaneamente troppo rigida (prima del carattere "@"), troppo vaga (dopo il carattere "@") e troppo lassista (che consente commenti , i caratteri di spazi bianchi e le stringhe citate in modi non familiari alla maggior parte degli utenti) per essere utili qui. [...] La seguente espressione regolare compatibile con JavaScript e Perl è un'implementazione della definizione sopra.

/^[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])?)*$/

Questo è probabilmente quello che vuoi, e se è abbastanza buono per HTML5, probabilmente è abbastanza buono per te. Possiamo utilizzarlo direttamente in PostgreSQL. Uso anche citextqui (che tecnicamente significa che puoi semplicemente regex un po 'visivamente rimuovendo il maiuscolo o il minuscolo).

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])?)*$' );

Ora puoi fare ...

SELECT 'asdf@foobar.com'::email;

Ma no

SELECT 'asdf@foob,,ar.com'::email;
SELECT 'asd@f@foobar.com'::email;

Perché entrambi ritornano

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

Perché anche questo è basato su citext

SELECT 'asdf@foobar.com'::email = 'ASdf@fooBAR.com';

restituisce true per impostazione predefinita.

Utilizzando plperlu/Email::Valid

Come nota importante, esiste un metodo più corretto per farlo che è molto più complesso plperlu. Se hai bisogno di questo livello di correttezza non lo desideri citext. Email::Validpuò anche verificare se il dominio ha un record MX (esempio nei documenti di Email :: Valido)! Innanzitutto, aggiungi plperlu (richiede un superutente).

CREATE EXTENSION plperlu;

Quindi creare la funzione , notare che contrassegniamo come 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';
$$;

Quindi crea il dominio ,

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

Le note

  1. L'uso citextè tecnicamente sbagliato. SMTP definisce local-partla distinzione tra maiuscole e minuscole. Ma, di nuovo, questo è un caso in cui le specifiche sono stupide. Contiene le proprie crisi d'identità. La specifica dice local-part(la parte prima del @) "PUO 'essere sensibile al maiuscolo / minuscolo" ... "DEVE ESSERE trattata come sensibile al maiuscolo / minuscolo" ... eppure "lo sfruttamento della distinzione maiuscole / minuscole delle parti locali delle cassette postali impedisce l'interoperabilità ed è scoraggiato".
  2. Le specifiche per un indirizzo e-mail sono così complesse che non sono neppure autonome. Il complesso è veramente un eufemismo, quelli che fanno le specifiche non lo capiscono nemmeno. . Dai documenti su regular-expression.info

    Nessuno di questi regex impone limiti di lunghezza sull'indirizzo e-mail complessivo o sulla parte locale o sui nomi di dominio. RFC 5322 non specifica alcuna limitazione di lunghezza. Questi derivano da limitazioni in altri protocolli come il protocollo SMTP per l'invio effettivo di e-mail. RFC 1035 afferma che i domini devono contenere 63 caratteri o meno, ma non li include nelle specifiche della sintassi. Il motivo è che un vero linguaggio regolare non può imporre un limite di lunghezza e non consentire trattini consecutivi allo stesso tempo.


1
Il collegamento W3.org è interrotto; ecco una fonte alternativa: html.spec.whatwg.org/multipage/…
MaxGabriel

@MaxGabriel grazie restate in giro, otterrete i permessi di modifica abbastanza presto, lo riparerò lì.
Evan Carroll,

C'è una ragione per avere entrambi a-ze A-Znelle classi di caratteri?
xehpuk,

@xehpuk bene, perché ~fa distinzione tra maiuscole e minuscole devi (a) usare la ~*distinzione tra maiuscole e minuscole o (b) avere le lettere maiuscole e minuscole nella classe di caratteri.
Evan Carroll,

citext's ~sembra essere per me maiuscole e minuscole, è per questo che sto chiedendo.
xehpuk,

46

Uso sempre l' CITEXTe-mail, perché un indirizzo e-mail (in pratica) non fa distinzione tra maiuscole e minuscole , ovvero John@Example.com è uguale a john@example.com.

È anche più semplice impostare un indice univoco per evitare duplicati, rispetto al testo:

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

Il confronto delle e-mail è anche più semplice e meno soggetto a errori:

SELECT * FROM address WHERE email = 'JOHN@example.com';

paragonato a:

SELECT * FROM address WHERE lower(email) = lower('JOHN@example.com');

CITEXTè un tipo definito in un modulo di estensione standard denominato "citext" e disponibile digitando:

CREATE EXTENSION citext;

PS texte varcharsono praticamente gli stessi in Postgres e non ci sono penalità per l'utilizzo textcome ci si potrebbe aspettare. Controlla questa risposta: differenza tra text e varchar


10

Uso sempre varchar(254)come un indirizzo e-mail non può essere più lungo di 254 caratteri.

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

Postgresql non ha un tipo incorporato per gli indirizzi e-mail, anche se mi sono imbattuto in alcuni tipi di dati forniti.

Inoltre, potresti voler aggiungere un trigger o una tale logica per standardizzare gli indirizzi e-mail nel caso in cui desideri aggiungere una chiave univoca su di esso.

In particolare, la domainparte dell'indirizzo e-mail (che è nella forma local-part@ non domainfa distinzione tra maiuscole e minuscole mentre local-partdeve essere trattata come maiuscole / minuscole. Vedi http://tools.ietf.org/html/rfc5321#section-2.4

Un'altra considerazione è se si desidera memorizzare nomi e indirizzi e-mail nel modulo "Joe Bloggs" <joe.bloggs@hotmail.com>, nel qual caso è necessaria una stringa più lunga di 254 caratteri e non sarà possibile utilizzare in modo significativo un vincolo univoco. Non lo farei e suggerirei di memorizzare il nome e l'indirizzo e-mail separatamente. Indirizzi di stampa piuttosto in questo formato sono sempre possibili nel tuo livello di presentazione.


Secondo 4.5.3.1. Limiti di dimensione e minimi , la lunghezza massima è di 320 caratteri (incluso il @).
Andriy M

1
@AndriyM Non c'è nulla nella sezione referenziata che dice 320. E comunque è sbagliato; tools.ietf.org/html/rfc5321#section-4.5.3.1.3 afferma che la lunghezza massima di un percorso è di 256 caratteri e che deve includere i "<" e ">" circostanti per un massimo di 254.
Colin Hart

Sono arrivato a 320 come massimo basato su 4.5.3.1.1 ("La lunghezza totale massima di un nome utente o altra parte locale è 64 ottetti") e 4.5.3.1.2 ("La lunghezza totale massima di un nome di dominio o il numero è 255 ottetti "). Quindi, 64 + 255 + 1 (the @) = 320. Forse sto interpretando male.
Andriy M

3
@AndriyM Leggi la risposta accettata alla domanda a cui ho collegato. Spiega tutto. Sono decisamente 254, e non 320.
Colin 't Hart

3

Potresti essere interessato all'utilizzo di un controllo VINCOLO (forse più semplice, ma potresti rifiutare più di quanto vorresti, oppure utilizzare una FUNZIONE, discussa qui e qui . Bascialmente, si tratta di compromessi tra specificità e facilità di implementazione. Argomento interessante però. PostgreSQL ha anche un tipo di indirizzo IP nativa, ma c'è un progetto su pgfoundry per un tipo di dati e-mail qui . Tuttavia, il migliore che ho trovato di questo è una e-mail di dominio. Il dominio è migliore di un vincolo di controllo perché se lo si modifica, è necessario farlo una sola volta nella definizione del dominio e non seguire le tracce delle tabelle padre-figlio modificando tutti i vincoli di controllo. I domini sono davvero fantastici - un po 'come i tipi di dati, ma più semplici da implementare. Li ho usati in Firebird - Oracle non li ha nemmeno!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.