Який найкращий спосіб зберігати адресу електронної пошти в PostgreSQL?


40

Який би був правильний тип даних для зберігання електронних адрес у PostgreSQL?

Я можу використовувати varchar(або навіть text), але мені цікаво, чи є більш конкретний тип даних для електронних листів.

Відповіді:


38

Спеціальні 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 (наприклад, в документах електронної пошти :: Дійсні)! По-перше, додайте 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. Специфікація електронної адреси настільки складна, що навіть не є самодостатньою. Комплекс справді занижений, ті , хто робить специфікацію, навіть не розуміють цього. . З документів на regular-expression.info

    Жоден із цих регексів не застосовує обмежень по довжині загальної адреси електронної пошти або локальної частини або імен домену. RFC 5322 не визначає жодних обмежень по довжині. Вони випливають з обмежень в інших протоколах, таких як протокол SMTP для фактичного надсилання електронної пошти. RFC 1035 зазначає, що домени повинні містити 63 символи або менше, але не включає це в специфікацію синтаксису. Причина полягає в тому, що справжня звичайна мова не може забезпечити обмеження довжини і одночасно заборонити послідовні дефіси.


1
Посилання W3.org розірвано; ось альтернативне джерело: html.spec.whatwg.org/multipage/…
MaxGabriel

@MaxGabriel спасибі, ви отримаєте правки для редагування досить швидко, і я зафіксую її там.
Еван Керролл

Чи є причина мати a-zі A-Zв класах персонажів, і в них?
xehpuk

@xehpuk добре, оскільки з урахуванням ~регістру вам потрібно (а) використовувати ~*нечутливі регістрові регістри, або (b) мати великі та малі літери в 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як можна було очікувати, немає жодного штрафу . Перевірте цю відповідь: Різниця між текстом і варшаром


10

Я завжди використовую varchar(254)як адресу електронної пошти не більше 254 символів.

Дивіться https://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-adreress

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 Харт

Я отримав 320 як максимум на основі 4.5.3.1.1 ("Максимальна загальна довжина імені користувача чи іншої локальної частини - 64 октети") та 4.5.3.1.2 ("Максимальна загальна довжина доменного імені або число - 255 октетів "). Отже, 64 + 255 + 1 (the @) = 320. Можливо, я його неправильно трактую.
Андрій М

3
@AndriyM Прочитайте прийняту відповідь на питання, до якого я пов’язаний. Це все пояснює. Це однозначно 254, а не 320.
Колін 'Харт

3

Можливо, вам буде цікаво використовувати чек CONSTRAINT (можливо, простіше, але ви можете відхилити більше, ніж ви хочете, або ви використовуєте FUNCTION, що обговорюється тут і тут . В основному, це все про компроміси між специфікою та простотою реалізації. Цікава тема хоча. PostgreSQL навіть має нативний IP тип адреси, але є проект по pgfoundry для типу даних по електронній пошті тут . Тим НЕ менше, кращий , що я знайшов про це лист домен. Домен краще, ніж обмеження перевірки, тому що якщо ви його зміните, вам доведеться робити це лише один раз у визначенні домену, а не слідувати слідам за таблицями батьків-дочір, змінюючи всі ваші обмеження перевірки. Домени дійсно класні - начебто типи даних, але простіші у виконанні. Я використовував їх у Firebird - Oracle навіть їх не має!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.