Каков наилучший способ хранения адреса электронной почты в PostgreSQL?

40

Какой тип данных подходит для хранения адресов электронной почты в PostgreSQL?

Я могу использовать varchar(или даже text), но мне интересно, есть ли более конкретный тип данных для электронных писем.

Адам Матан
источник

Ответы:

38

Custom DOMAINs

Я не думаю, что использования citext(без учета регистра) достаточно [1] . Используя PostgreSQL, мы можем создать собственный домен, который, по сути, представляет собой определенные ограничения для типа . Мы можем создать домен, например, над citextтипом или более text.

Использование type=emailспецификации HTML5

В настоящее время наиболее правильный ответ на вопрос, что такое адрес электронной почты, указан в RFC5322 . Эта спецификация безумно сложна [2] , настолько, что все ломает ее. HTML5 содержит другую спецификацию для электронной почты ,

Это требование является преднамеренным нарушением RFC 5322, который определяет синтаксис для адресов электронной почты, который является одновременно слишком строгим (до символа "@"), слишком расплывчатым (после символа "@") и слишком слабым (допускает комментарии , пробельные символы и строки в кавычках способами, незнакомыми большинству пользователей) для практического использования здесь. [...] Следующее JavaScript- и Perl-совместимое регулярное выражение является реализацией приведенного выше определения.

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

Это, вероятно, то, что вы хотите, и если он достаточно хорош для HTML5, то, вероятно, достаточно хорош для вас Мы можем использовать это непосредственно в PostgreSQL. Я также использую citextздесь (что технически означает, что вы можете просто визуально немного откорректировать, удалив либо верхний, либо нижний регистр).

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

Теперь вы можете сделать ...

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

Но нет

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

Потому что оба возвращаются

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

Потому что это тоже основано на citext

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

возвращает true по умолчанию.

Использование plperlu/Email::Valid

Важным примечанием является то, что существует более правильный метод, который гораздо сложнее использовать plperlu. Если вам нужен этот уровень корректности, вы не хотите citext. Email::ValidМожно даже проверить, есть ли в домене запись MX (например, в документах Email :: Valid)! Сначала добавьте plperlu (требуется суперпользователь).

CREATE EXTENSION plperlu;

Затем создайте функцию , обратите внимание, что мы помечаем как 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';
$$;

Затем создайте домен ,

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

Сноски

  1. Использование citextтехнически неправильно. SMTP определяется local-partкак чувствительный к регистру. Но, опять же, это тот случай, когда спецификация глупа. Это содержит свои собственные кризисы идентичности. Спецификация говорит local-part(часть перед@ ) «МОЖЕТ быть чувствительной к регистру» ... «ДОЛЖНА БЫТЬ рассматриваться как чувствительная к регистру» ... и все же «использование чувствительности к регистру локальных частей почтового ящика препятствует взаимодействию и не рекомендуется».
  2. Спецификация для адреса электронной почты настолько сложна, что даже не является автономной. Комплекс действительно преуменьшает, те , кто делает спецификацию, даже не понимают этого. , Из документов на регулярном

    Ни одно из этих регулярных выражений не налагает ограничений на длину общего адреса электронной почты, локальной части или доменных имен. RFC 5322 не устанавливает ограничений по длине. Они проистекают из ограничений других протоколов, таких как протокол SMTP для фактической отправки электронной почты. RFC 1035 заявляет, что домены должны содержать не более 63 символов, но не включает их в свою спецификацию синтаксиса. Причина в том, что настоящий регулярный язык не может одновременно устанавливать ограничение длины и запрещать последовательные дефисы.

Эван Кэрролл
источник
1
Ссылка на W3.org не работает; вот альтернативный источник: html.spec.whatwg.org/multipage/…
MaxGabriel
@MaxGabriel, спасибо, держитесь, вы получите разрешение на редактирование достаточно скоро, я исправлю его там.
Эван Кэрролл
Есть ли причина иметь и то a-zи другое A-Zв классах персонажей?
xehpuk
@xehpuk хорошо, потому что с ~учетом регистра вы должны (а) использовать ~*регистр без учета регистра или (б) иметь буквы верхнего и нижнего регистра в классе char.
Эван Кэрролл
citext«s , ~кажется, нечувствительны к регистру для меня, поэтому я спрашиваю.
xehpuk
46

Я всегда использую CITEXTдля электронной почты, потому что адрес электронной почты (на практике) нечувствителен к регистру , то есть John@Example.com совпадает с john@example.com.

Также проще настроить уникальный индекс для предотвращения дублирования по сравнению с текстом:

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

Сравнение электронных писем также проще и менее подвержено ошибкам:

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

по сравнению с:

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

CITEXTэто тип, определенный в стандартном модуле расширения с именем "citext" и доступный путем ввода:

CREATE EXTENSION citext;

PS text и varcharв Postgres они практически одинаковы, и штраф за использование, textкак и следовало ожидать, отсутствует. Проверьте этот ответ: Разница между текстом и varchar

гегемон
источник
10

Я всегда использую varchar(254)в качестве адреса электронной почты не более 254 символов.

Видеть Https://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address

Postgresql не имеет встроенного типа для адресов электронной почты, хотя я сталкивался с некоторыми типами данных.

Кроме того, вы можете добавить триггер или другую логику для стандартизации адресов электронной почты на случай, если вы захотите добавить уникальный ключ к нему.

В частности, domainчасть адреса электронной почты (которая имеет форму local-part@, domainнечувствительна к регистру, хотя local-partдолжна рассматриваться как чувствительная к регистру. См. Http://tools.ietf.org/html/rfc5321#section-2.4

Еще одно соображение - если вы хотите сохранить имена и адреса электронной почты в форме "Joe Bloggs" <joe.bloggs@hotmail.com>, в этом случае вам понадобится строка длиной более 254 символов, и вы не сможете осмысленно использовать уникальное ограничение. Я бы не стал делать это и предлагал бы хранить имя и адрес электронной почты отдельно. Красивая печать адресов в этом формате всегда возможна на вашем уровне представления.

Колин т Харт
источник
Согласно 4.5.3.1. Пределы размера и минимумы , максимальная длина составляет 320 символов (включая @).
Андрей М
1
@AndriyM В указанном разделе нет ничего, что говорит 320. И это в любом случае неправильно; tools.ietf.org/html/rfc5321#section-4.5.3.1.3 утверждает, что максимальная длина пути составляет 256 символов, и что должна включать в себя окружающие "<" и ">", составляющие максимум 254.
Колин 't Hart
Максимум 320 я достиг на основе 4.5.3.1.1 («Максимальная общая длина имени пользователя или другой локальной части составляет 64 октета») и 4.5.3.1.2 («Максимальная общая длина имени домена»). или число 255 октетов »). Итак, 64 + 255 + 1 (the @) = 320. Возможно, я неверно истолковываю это.
Андрей М
3
@AndriyM Прочитайте принятый ответ на вопрос, с которым я связан. Это все объясняет. Это определенно 254, а не 320.
Colin 't Hart
3

Возможно, вам будет интересно использовать проверку CONSTRAINT (возможно, проще, но вы можете отклонить больше, чем вы хотели бы, или вы используете FUNCTION, обсуждаемые здесь и здесь . По сути, все дело в компромиссе между спецификой и простотой реализации. Интересная тема хотя. PostgreSQL даже имеет нативную IP тип адреса, но есть проект по pgfoundry для типа данных по электронной почте здесь . Тем не менее, лучшее , что я нашел про это письмо домен, Домен лучше, чем проверочное ограничение, потому что если вы измените его, вам нужно будет сделать это только один раз в определении домена, а не следовать следам таблиц типа «родители-потомки», изменяя все ваши проверочные ограничения. Домены действительно крутые - вроде как типы данных, но проще в реализации. Я использовал их в Firebird - у Oracle их даже нет!

Verace
источник