web-dev-qa-db-fra.com

Quelle est la meilleure façon de stocker une adresse e-mail dans PostgreSQL?

Quel serait le bon type de données pour stocker les adresses e-mail dans PostgreSQL?

Je peux utiliser varchar (ou même text), mais je me demande s'il existe un type de données plus spécifique pour les e-mails.

42
Adam Matan

DOMAINs personnalisé

Je ne pense pas que l'utilisation de citext (insensible à la casse) soit suffisante[1]. En utilisant PostgreSQL, nous pouvons créer un domaine personnalisé qui est essentiellement des contraintes définies sur un type. Nous pouvons créer un domaine par exemple sur le type citext, ou sur text.

Utilisation de HTML5 type=email spec

Actuellement, la réponse la plus correcte à la question qu'est-ce qu'une adresse e-mail est spécifiée dans RFC5322 . Cette spécification est incroyablement complexe[2], à tel point que tout le casse. HTML5 contient une spécification différente pour le courrier électronique ,

Cette exigence est une violation délibérée de la RFC 5322, qui définit une syntaxe pour les adresses électroniques qui est simultanément trop stricte (avant le caractère "@"), trop vague (après le "@" ) et trop laxiste (autorisant les commentaires, les espaces et les chaînes entre guillemets d'une manière inconnue de la plupart des utilisateurs) pour être d'une utilité pratique ici. [...] Le code JavaScript et Perl suivant -une expression régulière compatible est une implémentation de la définition ci-dessus.

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

C'est probablement ce que vous voulez, et si c'est assez bon pour HTML5, c'est probablement assez bon pour vous. Nous pouvons l'utiliser directement dans PostgreSQL. J'utilise également citext ici (ce qui signifie techniquement que vous pouvez simplement regex un peu visuellement en supprimant les majuscules ou les minuscules).

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

Maintenant vous pouvez faire ...

SELECT '[email protected]'::email;

Mais non

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

Parce que les deux reviennent

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

Parce que c'est aussi basé sur citext

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

renvoie true par défaut.

Utilisation de plperlu / Email::Valid

Comme note importante, il existe une méthode plus correcte de faire cela qui est beaucoup plus complexe en utilisant plperlu. Si vous avez besoin de ce niveau d'exactitude, vous faites pas voulez citext. Email::Valid peut même vérifier si le domaine a un enregistrement MX (exemple dans les documents de Email :: Valid)! Tout d'abord, ajoutez plperlu (nécessite un superutilisateur).

CREATE EXTENSION plperlu;

Alors créez la fonction , notez que nous marquons en tant que 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';
$$;

Alors créez le domaine ,

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

Notes de bas de page

  1. L'utilisation de citext est techniquement incorrecte. SMTP définit local-part comme étant sensible à la casse. Mais, encore une fois, c'est un cas de la spécification étant stupide. Il contient ses propres crises d'identité. La spécification dit local-part (la partie avant le @) "PEUT être sensible à la casse" ... "DOIT ÊTRE traité comme sensible à la casse" ... et pourtant "exploiter la sensibilité à la casse des parties locales de la boîte aux lettres entrave l'interopérabilité et est déconseillé."
  2. La spécification d'une adresse e-mail est si complexe qu'elle n'est même pas autonome. Le complexe est vraiment un euphémisme, ceux qui font la spécification ne le comprennent même pas. . De la documentation sur regular-expression.info

    Aucune de ces expressions régulières n'applique de limites de longueur à l'adresse e-mail globale ou à la partie locale ou aux noms de domaine. RFC 5322 ne spécifie aucune limitation de longueur. Celles-ci découlent de limitations dans d'autres protocoles comme le protocole SMTP pour l'envoi de courriers électroniques. La RFC 1035 stipule que les domaines doivent contenir 63 caractères ou moins, mais ne les inclut pas dans sa spécification de syntaxe. La raison en est qu'un véritable langage régulier ne peut pas appliquer une limite de longueur et interdire les tirets consécutifs à le même temps.

40
Evan Carroll

J'utilise toujours CITEXT pour les e-mails, car une adresse e-mail est (en pratique) insensible à la casse , c'est-à-dire que [email protected] est identique à [email protected].

Il est également plus facile de configurer un index unique pour éviter les doublons, par rapport au texte:

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

La comparaison des e-mails est également plus facile et moins sujette aux erreurs:

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

comparé à:

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

CITEXT est un type défini dans un module d'extension standard nommé "citext" , et disponible en tapant:

CREATE EXTENSION citext;

P.S. text et varchar sont pratiquement les mêmes dans Postgres et il n'y a pas de pénalité pour utiliser text comme on peut s'y attendre. Cochez cette réponse: Différence entre le texte et varchar

47
hegemon

J'utilise toujours varchar(254) car une adresse e-mail ne doit pas dépasser 254 caractères.

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

Postgresql n'a pas de type intégré pour les adresses e-mail, bien que j'aie rencontré un type de données contribué.

En outre, vous souhaiterez peut-être ajouter un déclencheur ou une telle logique pour normaliser les adresses e-mail au cas où vous souhaiteriez y ajouter une clé unique.

En particulier, la partie domain de l'adresse e-mail (qui est de la forme local-part @ domain ne respecte pas la casse tandis que local-part doit être traité comme sensible à la casse. Voir http://tools.ietf.org/html/rfc5321#section-2.4

Si vous souhaitez stocker des noms et des adresses e-mail sous la forme "Joe Bloggs" <[email protected]>, auquel cas vous avez besoin d'une chaîne de plus de 254 caractères et vous ne pourrez pas utiliser de manière significative une contrainte unique. Je ne ferais pas cela et suggère de stocker le nom et l'adresse e-mail séparément. De jolies adresses d'impression dans ce format sont toujours possibles dans votre couche de présentation.

10
Colin 't Hart

Vous pourriez être intéressé par l'utilisation d'une vérification CONTRAINTE (peut-être plus facile, mais pourrait rejeter plus que vous ne le souhaiteriez, ou vous utilisez une FONCTION, discutée - ici et ici . Fondamentalement, il s'agit de compromis entre spécificité et facilité de mise en œuvre. Sujet intéressant cependant. PostgreSQL a même un type d'adresse IP natif, mais il existe un projet sur pgfoundry pour un type de données e-mail ici . Cependant, le meilleur que j'ai trouvé à ce sujet est un e-mail domaine . Le domaine est meilleur qu'une contrainte de vérification car si vous le changez, vous n'avez de le faire une fois dans la définition de domaine et de ne pas suivre les traces des tables parent-enfant en changeant toutes vos contraintes de vérification. Les domaines sont vraiment cool - un peu comme les types de données, mais plus simples à implémenter. Je les ai utilisés dans Firebird - Oracle n'a même pas leur!

3
Vérace