Apa cara terbaik untuk menyimpan alamat email di PostgreSQL?

40

Apa tipe data yang tepat untuk menyimpan alamat email di PostgreSQL?

Saya dapat menggunakan varchar(atau bahkan text), tapi saya ingin tahu apakah ada tipe data yang lebih spesifik untuk email.

Adam Matan
sumber

Jawaban:

38

kustom DOMAINs

Saya tidak berpikir menggunakan citext(case-insensitive) sudah cukup [1] . Menggunakan PostgreSQL kita dapat membuat domain kustom yang pada dasarnya adalah beberapa batasan yang ditentukan atas suatu jenis . Kami dapat membuat domain misalnya atas citextjenis, atau lebih text.

Menggunakan type=emailspec HTML5

Saat ini jawaban yang paling benar untuk pertanyaan apa alamat email yang ditentukan dalam RFC5322 . Spek itu sangat kompleks [2] , sedemikian rupa sehingga semuanya merusaknya. HTML5 berisi spesifikasi berbeda untuk email ,

Persyaratan ini adalah pelanggaran yang disengaja terhadap RFC 5322, yang mendefinisikan sintaks untuk alamat email yang secara bersamaan terlalu ketat (sebelum karakter "@"), terlalu kabur (setelah karakter "@"), dan terlalu longgar (memungkinkan komentar , karakter spasi putih, dan string kutipan dalam perilaku yang tidak dikenal oleh sebagian besar pengguna) agar praktis digunakan di sini. [...] Ekspresi reguler yang kompatibel dengan JavaScript dan Perl adalah implementasi dari definisi di atas.

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

Ini mungkin yang Anda inginkan, dan jika cukup baik untuk HTML5, mungkin cukup baik untuk Anda. Kita dapat menggunakannya secara langsung di PostgreSQL. Saya juga menggunakan di citextsini (yang secara teknis berarti Anda dapat dengan mudah regex sedikit secara visual dengan menghapus huruf besar atau kecil).

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

Sekarang kamu bisa melakukan ...

SELECT '[email protected]'::email;

Tapi tidak

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

Karena keduanya kembali

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

Karena ini juga didasarkan pada citext

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

mengembalikan true secara default.

Menggunakan plperlu/Email::Valid

Sebagai catatan penting, ada metode yang lebih tepat untuk melakukan ini yang menggunakan jauh lebih kompleks plperlu. Jika Anda membutuhkan tingkat kebenaran yang tidak Anda inginkan citext. Email::Validbahkan dapat memeriksa apakah domain memiliki data MX (contoh dalam dokumen Email :: Valid)! Pertama, tambahkan plperlu (membutuhkan superuser).

CREATE EXTENSION plperlu;

Kemudian buat fungsinya , perhatikan kami tandai di sebagai 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';
$$;

Lalu buat domain ,

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

Catatan kaki

  1. Penggunaannya citextsecara teknis salah. SMTP mendefinisikan local-partsebagai case sensitive. Tapi, sekali lagi, ini adalah kasus spec menjadi bodoh. Ini berisi krisis identitasnya sendiri. Spesifikasinya mengatakan local-part(bagian sebelum @) "MUNGKIN case-sensitive" ... "HARUS diperlakukan sebagai case case" ... namun "mengeksploitasi sensitivitas case bagian lokal kotak surat menghambat interoperabilitas dan tidak dianjurkan."
  2. Spesifikasi untuk alamat email sangat rumit, bahkan tidak lengkap. Kompleks benar-benar meremehkan, mereka yang membuat spec bahkan tidak memahaminya. . Dari dokumen di regular-expression.info

    Tidak satu pun dari regex ini yang menerapkan batasan panjang pada alamat email keseluruhan atau bagian lokal atau nama domain. RFC 5322 tidak menentukan batasan panjang apa pun. Mereka berasal dari keterbatasan dalam protokol lain seperti protokol SMTP untuk benar-benar mengirim email. RFC 1035 menyatakan bahwa domain harus berukuran 63 karakter atau kurang, tetapi tidak termasuk dalam spesifikasi sintaksisnya. Alasannya adalah bahwa bahasa reguler yang sebenarnya tidak dapat menegakkan batas panjang dan melarang tanda hubung berturut-turut pada saat yang sama.

Evan Carroll
sumber
1
Tautan W3.org rusak; inilah sumber alternatif: html.spec.whatwg.org/multipage/…
MaxGabriel
@ Maxgabriel terima kasih tetap, Anda akan segera mendapatkan izin edit saya akan memperbaikinya di sana.
Evan Carroll
Apakah ada alasan untuk memiliki keduanya a-zdan A-Zdi kelas karakter?
xehpuk
@ xehpuk yah, karena ~case-sensitive Anda harus (a) menggunakan ~*case-insensitive atau (b) memiliki huruf besar dan kecil di kelas char.
Evan Carroll
citext's ~tampaknya menjadi kasus-sensitif bagi saya, itu sebabnya aku bertanya.
xehpuk
46

Saya selalu menggunakan CITEXTuntuk email, karena alamat email (dalam praktiknya) tidak sensitif huruf besar , yaitu [email protected] sama dengan [email protected].

Lebih mudah mengatur indeks unik untuk mencegah duplikat, dibandingkan dengan teks:

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

Membandingkan email juga lebih mudah dan lebih rentan terhadap kesalahan:

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

jika dibandingkan dengan:

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

CITEXTadalah jenis yang didefinisikan dalam modul ekstensi standar bernama "citext" , dan tersedia dengan mengetik:

CREATE EXTENSION citext;

PS textdan varcharhampir sama di Postgres dan tidak ada penalti untuk digunakan textseperti yang diharapkan. Periksa jawaban ini: Perbedaan antara teks dan varchar

hegemon
sumber
10

Saya selalu menggunakan varchar(254)karena alamat email tidak boleh lebih dari 254 karakter.

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

Postgresql tidak memiliki tipe bawaan untuk alamat email, walaupun saya menemukan beberapa tipe data yang berkontribusi.

Selain itu, Anda mungkin ingin menambahkan pemicu atau logika semacam itu untuk membakukan alamat email jika Anda ingin menambahkan kunci unik di atasnya.

Secara khusus, domainbagian dari alamat email (yang dalam bentuk local-part@ domaintidak peka huruf besar-kecil sedangkan local-partharus diperlakukan sebagai peka huruf besar-kecil. Lihat http://tools.ietf.org/html/rfc5321#section-2.4

Pertimbangan lain adalah jika Anda ingin menyimpan nama dan alamat email dalam formulir "Joe Bloggs" <[email protected]>, dalam hal ini Anda membutuhkan string yang lebih panjang dari 254 karakter dan Anda tidak akan dapat menggunakan batasan unik. Saya tidak akan melakukan ini dan menyarankan untuk menyimpan nama dan alamat email secara terpisah. Alamat pencetakan cantik dalam format ini selalu memungkinkan di lapisan presentasi Anda.

Colin 't Hart
sumber
Menurut 4.5.3.1. Batas Ukuran dan Minimum , panjang maksimum adalah 320 karakter (termasuk @).
Andriy M
1
@AndriyM Tidak ada dalam bagian referensi yang mengatakan 320. Lagi pula itu salah; tools.ietf.org/html/rfc5321#section-4.5.3.1.3 menyatakan bahwa panjang maksimum path adalah 256 karakter, dan itu harus menyertakan "<" dan ">" sekitarnya yang menjadikan maksimum 254.
Colin Hart
Saya tiba di 320 sebagai maksimum berdasarkan 4.5.3.1.1 ("Panjang total maksimum nama pengguna atau bagian lokal lainnya adalah 64 oktet") dan 4.5.3.1.2 ("Panjang total maksimum nama domain atau jumlahnya adalah 255 oktet "). Jadi, 64 + 255 + 1 (the @) = 320. Mungkin saya salah mengartikannya.
Andriy M
3
@AndriyM Baca jawaban yang diterima untuk pertanyaan yang saya tautkan. Itu menjelaskan semuanya. Ini pasti 254, dan bukan 320.
Colin 't Hart
3

Anda mungkin tertarik menggunakan cek CONSTRAINT (mungkin lebih mudah, tetapi mungkin menolak lebih dari yang Anda inginkan, atau Anda menggunakan FUNGSI, dibahas di sini dan di sini . Pada dasarnya, ini semua tentang pengorbanan antara kekhususan dan kemudahan implementasi. Topik menarik PostgreSQL bahkan memiliki tipe alamat IP asli, tetapi ada proyek pada pgfoundry untuk tipe data email di sini . Namun, yang terbaik yang saya temukan tentang ini adalah domain email. Domain lebih baik daripada batasan cek karena jika Anda mengubahnya, Anda hanya perlu melakukannya sekali dalam definisi domain dan tidak mengikuti jejak menuruni tabel orangtua-anak mengubah semua kendala pemeriksaan Anda. Domain sangat keren - agak seperti tipe data, tetapi lebih mudah diterapkan. Saya menggunakannya di Firebird - Oracle bahkan tidak memilikinya!

Vérace
sumber