¿Cuál es la mejor manera de almacenar una dirección de correo electrónico en PostgreSQL?

40

¿Cuál sería el tipo de datos correcto para almacenar direcciones de correo electrónico en PostgreSQL?

Puedo usar varchar(o incluso text), pero me pregunto si hay un tipo de datos más específico para los correos electrónicos.

Adam Matan
fuente

Respuestas:

38

costumbre DOMAINs

No creo que usar citext(mayúsculas y minúsculas) sea suficiente [1] . Usando PostgreSQL podemos crear un dominio personalizado que es esencialmente algunas restricciones definidas sobre un tipo . Podemos crear un dominio, por ejemplo, sobre el citexttipo o sobre text.

Usando type=emailespecificaciones HTML5

Actualmente, la respuesta más correcta a la pregunta de qué es una dirección de correo electrónico se especifica en RFC5322 . Esa especificación es increíblemente compleja [2] , tanto que todo lo rompe. HTML5 contiene una especificación diferente para el correo electrónico ,

Este requisito es una violación intencional de RFC 5322, que define una sintaxis para las direcciones de correo electrónico que es simultáneamente demasiado estricta (antes del carácter "@"), demasiado vaga (después del carácter "@") y demasiado laxa (permitiendo comentarios , caracteres de espacios en blanco y cadenas citadas de maneras desconocidas para la mayoría de los usuarios) para que sean de utilidad práctica aquí. [...] La siguiente expresión regular compatible con JavaScript y Perl es una implementación de la definición anterior.

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

Esto es probablemente lo que desea, y si es lo suficientemente bueno para HTML5, probablemente sea lo suficientemente bueno para usted. Podemos hacer uso de eso directamente en PostgreSQL. También lo uso citextaquí (lo que técnicamente significa que puede simplemente regexar un poco visualmente eliminando las mayúsculas o 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])?)*$' );

Ahora puedes hacer ...

SELECT '[email protected]'::email;

Pero no

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

Porque ambos regresan

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

Porque esto también se basa en citext

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

devuelve verdadero por defecto.

Usando plperlu/Email::Valid

Como nota importante, hay un método más correcto para hacerlo que es mucho más complejo de usar plperlu. Si necesita este nivel de corrección, no lo desea citext. Email::Valid¡incluso puede verificar si el dominio tiene un registro MX (ejemplo en documentos de Email :: Valid)! Primero, agregue plperlu (requiere superusuario).

CREATE EXTENSION plperlu;

Luego cree la función , 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';
$$;

Luego crea el dominio ,

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

Notas al pie

  1. Usar citextes técnicamente incorrecto. SMTP define local-partcomo mayúsculas y minúsculas. Pero, de nuevo, este es un caso de que la especificación sea ​​estúpida. Contiene sus propias crisis de identidad. La especificación dice local-part(la parte anterior a @) "PUEDE ser sensible a mayúsculas y minúsculas" ... "DEBE SER tratada como sensible a mayúsculas y minúsculas" ... y aún así "explotar la mayúsculas y minúsculas de las partes locales del buzón impide la interoperabilidad".
  2. La especificación para una dirección de correo electrónico es tan compleja que ni siquiera es autónoma. El complejo es realmente un eufemismo, aquellos que hacen la especificación ni siquiera lo entienden. . De los documentos en regular-expression.info

    Ninguna de estas expresiones regulares impone límites de longitud en la dirección de correo electrónico general o en la parte local o los nombres de dominio. RFC 5322 no especifica ninguna limitación de longitud. Esos se derivan de limitaciones en otros protocolos como el protocolo SMTP para enviar correos electrónicos. RFC 1035 establece que los dominios deben tener 63 caracteres o menos, pero no incluye eso en su especificación de sintaxis. La razón es que un verdadero lenguaje regular no puede imponer un límite de longitud y no permitir guiones consecutivos al mismo tiempo.

Evan Carroll
fuente
1
El enlace W3.org está roto; aquí hay una fuente alternativa: html.spec.whatwg.org/multipage/…
MaxGabriel
@MaxGabriel gracias, quédate, recibirás los permisos de edición lo suficientemente pronto, lo arreglaré allí.
Evan Carroll
¿Hay alguna razón para tener ambas a-zy A-Zen las clases de personajes?
xehpuk
@xehpuk bien, porque distingue entre mayúsculas y minúsculas, debe ~(a) utilizar ~*mayúsculas y minúsculas o (b) tener las letras mayúsculas y minúsculas en la clase char.
Evan Carroll
citext's ~parece ser sensible a las mayúsculas para mí, es por eso que estoy pidiendo.
xehpuk
46

Siempre uso CITEXTpara correo electrónico, porque una dirección de correo electrónico no distingue entre mayúsculas y minúsculas , es decir, [email protected] es igual a [email protected]

También es más fácil configurar un índice único para evitar duplicados, en comparación con el 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 correos electrónicos también es más fácil y menos propenso a errores:

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

en comparación con:

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

CITEXTes un tipo definido en un módulo de extensión estándar llamado "citext" , y disponible escribiendo:

CREATE EXTENSION citext;

PD texty varcharson prácticamente iguales en Postgres y no hay penalización por usar textcomo uno podría esperar. Verifique esta respuesta: Diferencia entre texto y varchar

hegemon
fuente
10

Siempre uso varchar(254)como dirección de correo electrónico no puede tener más de 254 caracteres.

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

Postgresql no tiene un tipo incorporado para las direcciones de correo electrónico, aunque encontré algún tipo de datos contribuido.

Además, es posible que desee agregar un disparador o alguna lógica para estandarizar las direcciones de correo electrónico en caso de que desee agregar una clave única.

En particular, la domainparte de la dirección de correo electrónico (que tiene la forma local-part@ no domaindistingue entre mayúsculas y minúsculas y local-partdebe tratarse como mayúsculas y minúsculas). Consulte http://tools.ietf.org/html/rfc5321#section-2.4

Otra consideración es si desea almacenar nombres y direcciones de correo electrónico en el formulario "Joe Bloggs" <[email protected]>, en cuyo caso necesita una cadena de más de 254 caracteres y no podrá usar significativamente una restricción única. No haría esto y sugeriría almacenar el nombre y la dirección de correo electrónico por separado. Las direcciones de impresión bonitas en este formato siempre son posibles en su capa de presentación.

Colin 't Hart
fuente
De acuerdo con 4.5.3.1. Límites de tamaño y mínimos , la longitud máxima es de 320 caracteres (incluido el @).
Andriy M
1
@AndriyM No hay nada en la sección de referencia que diga 320. Y eso está mal de todos modos; tools.ietf.org/html/rfc5321#section-4.5.3.1.3 establece que la longitud máxima de una ruta es de 256 caracteres y que debe incluir los "<" y ">" circundantes, lo que hace que el máximo sea 254.
Colin 't Hart
Llegué a 320 como máximo basado en 4.5.3.1.1 ("La longitud total máxima de un nombre de usuario u otra parte local es de 64 octetos") y 4.5.3.1.2 ("La longitud total máxima de un nombre de dominio o el número es 255 octetos "). Entonces, 64 + 255 + 1 (the @) = 320. Quizás lo estoy malinterpretando.
Andriy M
3
@AndriyM Lea la respuesta aceptada a la pregunta a la que me vinculé. Lo explica todo. Definitivamente es 254, y no 320.
Colin 't Hart
3

Puede que le interese usar un control CONSTRAINT (posiblemente más fácil, pero puede rechazar más de lo que desea, o usar una FUNCIÓN, discutida aquí y aquí . Básicamente, se trata de compensaciones entre la especificidad y la facilidad de implementación. Tema interesante Sin embargo, PostgreSQL incluso tiene un tipo de dirección IP nativa, pero hay un proyecto en pgfoundry para un tipo de datos de correo electrónico aquí . Sin embargo, lo mejor que encontré sobre esto es un dominio de correo electrónico. El dominio es mejor que una restricción de verificación porque si lo cambia, solo tiene que hacerlo una vez en la definición del dominio y no seguir los rastros de las tablas padre-hijo cambiando todas sus restricciones de verificación. Los dominios son realmente geniales, como los tipos de datos, pero más simples de implementar. Los usé en Firebird: ¡Oracle ni siquiera los tiene!

Vérace
fuente