Quel est le meilleur moyen de stocker une adresse email dans PostgreSQL?

40

Quel serait le bon type de données pour stocker les adresses électroniques 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 courriels.

Adam Matan
la source

Réponses:

38

personnalisé DOMAINs

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 constitué de contraintes définies sur un type . Nous pouvons créer un domaine par exemple sur le citexttype ou sur text.

Utilisation de la type=emailspécification HTML5

Actuellement, la réponse la plus correcte à la question de savoir ce qu’est une adresse électronique est spécifiée dans la 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 volontaire de la RFC 5322, qui définit une syntaxe pour les adresses électroniques simultanément à la fois trop stricte (avant le caractère "@"), trop vague (après le caractère "@") et trop laxiste (permettant des commentaires). , les caractères d’espace, et les chaînes citées de manière inconnue de la plupart des utilisateurs) sont ici utiles. [...] L'expression régulière suivante compatible JavaScript et Perl 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 suffisant pour HTML5, il l'est probablement pour vous. Nous pouvons l'utiliser directement dans PostgreSQL. J'utilise aussi citextici (ce qui signifie techniquement que vous pouvez simplement visionner légèrement l'expression régulière 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 tu peux faire ...

SELECT '[email protected]'::email;

Mais non

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

Parce que tous les deux reviennent

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

Parce que c'est aussi basé sur le citext

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

renvoie vrai par défaut.

Utiliser plperlu/Email::Valid

Il est important de noter qu’il existe une méthode plus correcte, beaucoup plus complexe, pour ce faire plperlu. Si vous avez besoin de ce niveau de correction, vous ne voulez pascitext . Email::Validpeut même vérifier si le domaine a un enregistrement MX (exemple dans la documentation de Email :: Valid)! Premièrement, ajoutez plperlu (nécessite le superutilisateur).

CREATE EXTENSION plperlu;

Puis 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';
$$;

Puis 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 citextest techniquement incorrecte. SMTP définit local-partcomme étant sensible à la casse. Mais, encore une fois, c’est un cas de spécification stupide. Il contient ses propres crises d'identité. La spécification indique local-part(la partie antérieure à @) "PEUT être sensible à la casse" ... "DOIT ÊTRE traitée comme sensible à la casse" ... et pourtant "l'exploitation de la sensibilité à la casse des parties locales de boîte aux lettres entrave l'interopérabilité et est découragée."
  2. La spécification pour une adresse e-mail est si complexe qu'elle n'est même pas autonome. Complexe est vraiment un euphémisme, ceux qui font la spécification ne le comprennent même pas. . De la docs sur regular-expression.info

    Aucune de ces expressions rationnelles n'applique de limite de longueur à l'adresse électronique globale, à la partie locale ou aux noms de domaine. La RFC 5322 ne spécifie aucune limite de longueur. Celles-ci découlent des limitations d'autres protocoles tels que le protocole SMTP pour l'envoi de courrier électronique. La RFC 1035 précise que les domaines doivent comporter 63 caractères au maximum, mais ne l'inclut pas dans sa spécification de syntaxe. La raison en est qu'un vrai langage normal ne peut pas imposer une limite de longueur et interdire les traits d'union consécutifs en même temps.

Evan Carroll
la source
1
Le lien W3.org est cassé; voici une source alternative: html.spec.whatwg.org/multipage/…
MaxGabriel
@ MaxGabriel, restez dans les parages, vous obtiendrez les modifications permanentes assez rapidement.
Evan Carroll
Y a-t-il une raison d'avoir les deux a-zet A-Zdans les classes de personnage?
xehpuk
@xehpuk bien, parce que ~vous êtes sensible à la casse, vous devez soit (a) ne pas utiliser de distinction de ~*casse ou (b) avoir les lettres majuscules et minuscules dans la classe char.
Evan Carroll
citext« s ~semble être insensible à la casse pour moi, c'est la raison pour laquelle je demande.
xehpuk
46

J'utilise toujours CITEXTpour le courrier électronique, car une adresse électronique 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)));

Comparer des courriels est également plus facile et moins sujet aux erreurs:

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

comparé à:

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

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

CREATE EXTENSION citext;

PS textet varcharsont pratiquement les mêmes dans Postgres et il n'y a pas de pénalité pour utiliser textcomme on peut s'y attendre. Cochez cette réponse: Différence entre text et varchar

hégémon
la source
10

J'utilise toujours varchar(254)comme adresse e-mail ne peut 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 je sois tombé sur un type de données ajouté.

De plus, vous pouvez souhaiter ajouter un déclencheur ou une telle logique pour normaliser les adresses électroniques au cas où vous souhaiteriez ajouter une clé unique.

En particulier, la domainpartie de l'adresse électronique (de la forme local-part@ domainest insensible à la casse, mais local-partdoit être traitée comme telle). Voir http://tools.ietf.org/html/rfc5321#section-2.4

Vous pouvez également enregistrer les noms et les adresses électroniques dans le formulaire "Joe Bloggs" <[email protected]>. Dans ce cas, vous devez disposer 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 le ferais pas et suggérerais de stocker le nom et l'adresse électronique séparément. De jolies adresses d'impression dans ce format sont toujours possibles dans votre couche présentation.

Colin 't Hart
la source
Selon 4.5.3.1. Limites et minimums de taille , la longueur maximale est de 320 caractères (y compris les @).
Andriy M
1
@AndriyM Il n'y a rien dans la section référencée qui dit 320. Et c'est faux quand même; tools.ietf.org/html/rfc5321#section-4.5.3.1.3 indique que la longueur maximale d'un chemin est de 256 caractères et qu'elle doit inclure les "<" et ">" qui l'entourent, soit un maximum de 254.
Colin pas Hart
Je suis arrivé à 320 au maximum basé sur 4.5.3.1.1 ("La longueur totale maximale d'un nom d'utilisateur ou une autre partie locale est de 64 octets") et 4.5.3.1.2 ("La longueur totale maximale d'un nom de domaine ou le nombre est 255 octets "). Donc, 64 + 255 + 1 (le @) = 320. Peut-être que je l’interprète mal.
Andriy M
3
@AndriyM Lisez la réponse acceptée à la question à laquelle j'ai lié. Cela explique tout. Il est définitivement 254 et non 320.
Colin 't Hart
3

Vous pourriez être intéressé par un chèque CONTRAINT (peut-être plus facile, mais vous pourriez en rejeter plus que vous ne le souhaitez, ou bien vous utilisez une fonction, abordée ici et ici . Fondamentalement, il s’agit essentiellement de compromis entre spécificité et facilité d’implémentation. 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 de courrier électronique ici . Cependant, le meilleur que j'ai trouvé à ce sujet est un domaine de courrier électronique.. Le domaine est préférable à une contrainte de vérification, car si vous le modifiez, il vous suffit de le faire une fois dans la définition du domaine et de ne pas suivre les traces des tables parent-enfant en modifiant toutes vos contraintes de vérification. Les domaines sont vraiment cool - un peu comme les types de données, mais plus simple à implémenter. Je les ai utilisés dans Firebird - Oracle ne les a même pas!

Vérace
la source