Was ist der beste Weg, um eine E-Mail-Adresse in PostgreSQL zu speichern?

40

Was wäre der richtige Datentyp, um E-Mail-Adressen in PostgreSQL zu speichern?

Ich kann varchar(oder sogar text) verwenden, aber ich frage mich, ob es einen spezifischeren Datentyp für E-Mails gibt.

Adam Matan
quelle

Antworten:

38

Sitte DOMAINs

Ich denke nicht, dass die Verwendung von citext(Groß- / Kleinschreibung wird nicht berücksichtigt) ausreicht [1] . Mit PostgreSQL können wir eine benutzerdefinierte Domäne erstellen, bei der es sich im Wesentlichen um einige definierte Einschränkungen für einen Typ handelt . Wir können eine Domain zum Beispiel über den citextTyp oder über erstellen text.

HTML5- type=emailSpezifikation verwenden

Die aktuell korrekteste Antwort auf die Frage, was eine E-Mail-Adresse ist, ist in RFC5322 angegeben . Diese Spezifikation ist wahnsinnig komplex [2] , so sehr, dass alles kaputt geht. HTML5 enthält eine andere Spezifikation für E-Mail ,

Diese Anforderung stellt eine vorsätzliche Verletzung von RFC 5322 dar, das eine Syntax für E-Mail-Adressen definiert, die gleichzeitig zu streng (vor dem Zeichen "@"), zu vage (nach dem Zeichen "@") und zu lasch (Kommentare zulassen) ist , Whitespace-Zeichen und Anführungszeichen in für die meisten Benutzer ungewohnter Weise), um hier von praktischem Nutzen zu sein. [...] Der folgende JavaScript- und Perl-kompatible reguläre Ausdruck ist eine Implementierung der obigen Definition.

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

Dies ist wahrscheinlich das, was Sie wollen, und wenn es gut genug für HTML5 ist, ist es wahrscheinlich gut genug für Sie. Das können wir direkt in PostgreSQL nutzen. Ich verwende auch citexthier (was technisch bedeutet, dass Sie die Regex einfach ein wenig visuell darstellen können, indem Sie entweder die Groß- oder Kleinschreibung entfernen).

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

Jetzt können Sie tun ...

SELECT '[email protected]'::email;

Aber nicht

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

Weil beide zurückkehren

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

Weil dies auch auf citext basiert

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

gibt standardmäßig true zurück.

Verwenden von plperlu/Email::Valid

Ein wichtiger Hinweis ist, dass es eine korrektere Methode gibt, die mit der Verwendung weitaus komplexer ist plperlu. Wenn Sie diesen Grad an Korrektheit benötigen , möchten Sie nichtcitext . Email::Validkann sogar überprüfen, ob die Domain einen MX-Eintrag hat (Beispiel in den Dokumenten von Email :: Valid)! Fügen Sie zunächst plperlu hinzu (Superuser erforderlich).

CREATE EXTENSION plperlu;

Dann die Funktion erstellen , bemerken wir als markieren 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';
$$;

Dann erstelle die Domain ,

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

Fußnoten

  1. Die Verwendung citextist technisch falsch. SMTP unterscheidet local-partzwischen Groß- und Kleinschreibung. Aber auch dies ist ein Fall, in dem die Spezifikation dumm ist. Es enthält seine eigenen Identitätskrisen. In der Spezifikation steht local-part(der Teil vor dem @) "MUSS zwischen Groß- und Kleinschreibung unterschieden werden" ... "MUSS zwischen Groß- und Kleinschreibung unterschieden werden" ... und dennoch "behindert das Ausnutzen der Groß- und Kleinschreibung lokaler Mailbox-Teile die Interoperabilität und wird davon abgeraten".
  2. Die Spezifikation für eine E-Mail-Adresse ist so komplex, dass sie nicht einmal in sich geschlossen ist. Komplex ist wirklich eine Untertreibung, diejenigen, die die Spezifikation machen, verstehen sie nicht einmal. . Aus den Dokumenten auf regular-expression.info

    Keine dieser regulären Ausdrücke erzwingt Längenbeschränkungen für die gesamte E-Mail-Adresse oder den lokalen Teil oder die Domain-Namen. RFC 5322 legt keine Längenbeschränkungen fest. Diese ergeben sich aus Einschränkungen in anderen Protokollen wie dem SMTP-Protokoll zum tatsächlichen Senden von E-Mails. RFC 1035 gibt an, dass Domänen maximal 63 Zeichen lang sein dürfen, schließt dies jedoch nicht in die Syntaxspezifikation ein. Der Grund dafür ist, dass eine echte reguläre Sprache kein Längenlimit erzwingen und gleichzeitig aufeinanderfolgende Bindestriche nicht zulassen kann.

Evan Carroll
quelle
1
Der W3.org-Link ist defekt. Hier ist eine alternative Quelle: html.spec.whatwg.org/multipage/…
MaxGabriel
@MaxGabriel danke bleib dran, du bekommst die Bearbeitungserlaubnis bald genug, ich werde sie dort reparieren lassen.
Evan Carroll
Gibt es einen Grund, beides a-zund A-Zin den Charakterklassen zu haben?
Xehpuk
@xehpuk gut, weil zwischen ~Groß- und Kleinschreibung unterschieden wird, muss entweder ~*(a) die Groß- und Kleinschreibung nicht beachtet werden oder (b) die Groß- und Kleinschreibung in der Zeichenklasse angegeben werden.
Evan Carroll
citext‚s ~scheint , Groß- und Kleinschreibung zu mir zu sein, das ist , warum ich frage.
Xehpuk
46

Ich verwende immer CITEXTfür E-Mails, da eine E-Mail-Adresse (in der Praxis) nicht zwischen Groß- und Kleinschreibung unterscheidet , dh [email protected] ist identisch mit [email protected].

Es ist auch einfacher, einen eindeutigen Index einzurichten, um Duplikate im Vergleich zu Text zu vermeiden:

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

Das Vergleichen von E-Mails ist außerdem einfacher und weniger fehleranfällig:

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

verglichen mit:

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

CITEXTist ein Typ, der in einem Standard-Erweiterungsmodul mit dem Namen "citext" definiert ist und durch Eingabe von:

CREATE EXTENSION citext;

PS textund varcharsind praktisch die gleichen in Postgres und es gibt keine Strafe für die Verwendung, textwie man erwarten kann. Überprüfen Sie diese Antwort: Unterschied zwischen Text und Varchar

Hegemon
quelle
10

Ich benutze immer varchar(254)als E-Mail-Adresse darf nicht länger als 254 Zeichen sein.

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

Postgresql hat keinen eingebauten Typ für E-Mail-Adressen, obwohl ich auf einen beitragenden Datentyp gestoßen bin.

Außerdem möchten Sie möglicherweise einen Auslöser oder eine solche Logik hinzufügen, um E-Mail-Adressen zu standardisieren, falls Sie einen eindeutigen Schlüssel hinzufügen möchten.

Insbesondere muss der domainTeil der E-Mail-Adresse (in der Form local-part@ zwischen domainGroß- und Kleinschreibung unterschieden werden, während zwischen Groß- und Kleinschreibung unterschieden werden local-partmuss). Siehe http://tools.ietf.org/html/rfc5321#section-2.4

Eine weitere Überlegung ist, ob Sie Namen und E-Mail-Adressen im Formular speichern möchten. "Joe Bloggs" <[email protected]>In diesem Fall benötigen Sie eine Zeichenfolge mit mehr als 254 Zeichen und können keine eindeutige Einschränkung sinnvoll verwenden. Ich würde dies nicht tun und empfehlen, Name und E-Mail-Adresse getrennt zu speichern. Hübsche Druckadressen in diesem Format sind in Ihrer Präsentationsebene immer möglich.

Colin 't Hart
quelle
Nach 4.5.3.1. Größenbeschränkungen und -minima , die maximale Länge beträgt 320 Zeichen (einschließlich der @).
Andriy M
1
@AndriyM In dem Abschnitt, auf den verwiesen wird, steht nichts, was 320 sagt. Und das ist sowieso falsch. tools.ietf.org/html/rfc5321#section-4.5.3.1.3 gibt an, dass die maximale Länge eines Pfads 256 Zeichen beträgt und dass die umgebenden Zeichen "<" und ">" die maximale Länge von 254 Zeichen enthalten müssen.
Colin 't Hart
Ich kam zu 320 als Maximum basierend auf 4.5.3.1.1 ("Die maximale Gesamtlänge eines Benutzernamens oder eines anderen lokalen Teils beträgt 64 Oktetts") und 4.5.3.1.2 ("Die maximale Gesamtlänge eines Domainnamens") oder Zahl ist 255 Oktette "). Also 64 + 255 + 1 (the @) = 320. Vielleicht interpretiere ich es falsch.
Andriy M
3
@AndriyM Lies die akzeptierte Antwort auf die Frage, mit der ich verlinkt bin. Das erklärt alles. Es ist definitiv 254 und nicht 320.
Colin 't Hart
3

Möglicherweise möchten Sie einen check CONSTRAINT verwenden (möglicherweise einfacher, aber lehnen Sie möglicherweise mehr ab, als Sie möchten, oder verwenden Sie eine hier und hier diskutierte FUNCTION. Grundsätzlich geht es um Kompromisse zwischen Spezifität und einfacher Implementierung. Interessantes Thema though. PostgreSQL hat auch einen native IP - Adresstyp, aber es ist ein Projekt auf pgfoundry für einen E - Mail - Datentyp hier . aber das beste , was ich dazu gefunden ist eine E - Mail - Domain. Die Domäne ist besser als eine Überprüfungsbedingung, da Sie sie bei einer Änderung in der Domänendefinition nur einmal ausführen müssen und nicht den Nachverfolgungen von Eltern-Kind-Tabellen folgen müssen, um alle Ihre Überprüfungsbedingungen zu ändern. Domänen sind wirklich cool - ein bisschen wie Datentypen, aber einfacher zu implementieren. Ich habe sie in Firebird verwendet - Oracle hat sie nicht einmal!

Vérace
quelle