--
-- create type email under text;
--
-- Elein Mustain, elein@varlena.com, http://www.varlena.com
-- Published 19-Feb-2006 at http://www.varlena.com/GeneralBits/128.php
-- License: BSD, Share and enjoy
--

drop domain email cascade;

--
-- Check constraint isemail for email base type
-- Using "pretty good" but not strict rfc compliant regex
-- cf: http://aspn.activestate.com/ASPN/Cookbook/Rx/Recipe/59886
--
CREATE OR REPLACE FUNCTION isemail(text)
RETURNS BOOLEAN
AS $$
   if ( $_[0] =~ m/
       ^                  # Start.
        (                 # Start of $1
            [A-Z0-9]+     # Must start with some A-Z or 0-9s.
            [._]?         # May have a . or an _ after that
        )                 # End of $1
        {1,}              # One or more of $1.
        [A-Z0-9]+         # More alphanumerics
        \@                # Literal @ sign
        (                 # Start of $2
            (             # Start of $3
                [A-Z0-9]+ # More alphanumerics
                [-]?      # At most one - sign.
            )             # End of $3
            {1,}          # One or more of $3
            [A-Z0-9]+\.   # More alphanumerics
        )                 # End of $2
        {1,}              # One or more of $2.
        [A-Z]{2,4}        # 2-4 letters
        $                 # End.
    /ix ) {
      return TRUE;
   }
   else {
      return FALSE;
   }
$$ LANGUAGE 'plperl' STRICT IMMUTABLE;

--
-- create type email under text
--
CREATE DOMAIN email AS text CHECK ( isemail( VALUE) );

--
-- Comparison and Equality Functions
--
-- cmp: 0 if equal, 1 if greater than, -1 if less than
CREATE OR REPLACE FUNCTION email_cmp(email,email)
RETURNS INTEGER AS
$$
	SELECT CASE WHEN lower($1) = lower($2) THEN 0
	            WHEN lower($1) > lower($2) THEN 1
	ELSE -1
	END;
$$ LANGUAGE 'SQL' IMMUTABLE STRICT;

-- Equals: lower(text) = lower(text)
CREATE OR REPLACE FUNCTION email_eq (email, email) RETURNS boolean AS
$$
	SELECT CASE WHEN email_cmp($1, $2) = 0 
	THEN TRUE ELSE FALSE END;
$$ LANGUAGE 'SQL' IMMUTABLE STRICT;

-- Greater than: lower(text) > lower(text)
CREATE OR REPLACE FUNCTION email_gt (email, email) RETURNS boolean AS
$$
	SELECT CASE WHEN email_cmp($1, $2) > 0 
	THEN TRUE ELSE FALSE END;
$$ LANGUAGE 'SQL' IMMUTABLE STRICT;

-- Less than: lower(text) < lower(text)
CREATE OR REPLACE FUNCTION email_lt (email, email) RETURNS boolean AS
$$
	SELECT CASE WHEN email_cmp($1, $2) < 0 
	THEN TRUE ELSE FALSE END;
$$ LANGUAGE 'SQL' IMMUTABLE STRICT;

--Greater than or Equals
CREATE OR REPLACE FUNCTION email_gte (email, email) RETURNS boolean AS
$$
	SELECT CASE WHEN email_cmp($1,$2) = 0 OR email_cmp($1,$2) = 1
	THEN TRUE ELSE FALSE end;
$$ LANGUAGE 'SQL' IMMUTABLE STRICT;

--Less than or Equals
CREATE OR REPLACE FUNCTION email_lte (email, email) RETURNS boolean AS
$$
	SELECT CASE WHEN email_cmp($1,$2) = 0 OR email_cmp($1,$2) = -1
	THEN TRUE ELSE FALSE END;
$$ LANGUAGE 'SQL' IMMUTABLE STRICT;

--Like
CREATE OR REPLACE FUNCTION email_like(email, text) RETURNS boolean AS
$$
	SELECT lower($1) LIKE lower($2);
$$ LANGUAGE 'SQL' IMMUTABLE STRICT;

--
-- Operators
--
CREATE OPERATOR =  ( PROCEDURE = email_eq, LEFTARG = email, RIGHTARG = email);
CREATE OPERATOR >  ( PROCEDURE = email_gt, LEFTARG = email, RIGHTARG = email);
CREATE OPERATOR >= ( PROCEDURE = email_gte, LEFTARG = email, RIGHTARG = email);
CREATE OPERATOR <  ( PROCEDURE = email_lt, LEFTARG = email, RIGHTARG = email);
CREATE OPERATOR <= ( PROCEDURE = email_lte, LEFTARG = email, RIGHTARG = email);
CREATE OPERATOR ~~  ( PROCEDURE = email_like, LEFTARG = email, RIGHTARG = text);

--
-- Operator Class
--
CREATE OPERATOR CLASS email_ops
DEFAULT FOR TYPE email USING BTREE AS
	OPERATOR 1 <,
	OPERATOR 2 <=, 
	OPERATOR 3 =,
	OPERATOR 4 >=,
	OPERATOR 5 >,
	FUNCTION 1 email_cmp(email,email)
;

--
-- Accessor functions
--
-- login: before the @ sign
CREATE OR REPLACE FUNCTION email_login (email) RETURNS text AS
$$
	return (split(/\@/, $_[0]))[0];
$$ LANGUAGE 'plperl' IMMUTABLE STRICT;

-- Full Domain: after the @ sign
CREATE OR REPLACE FUNCTION email_fdomain (email) RETURNS text AS
$$
	return  (split /\@/, $_[0])[-1];
$$ LANGUAGE 'plperl' IMMUTABLE STRICT;

-- Top Level Domain:: after the last dot
CREATE OR REPLACE FUNCTION email_domain_type (email) RETURNS text AS
$$
	return (split(   # Split the output of split below
        /\./,  # Split the following on "."
        (
            split(
                /\@/,  # Split whole input on "@"
                $_[0]
            )
        )[-1]  # last part of inner split
    ))[-1];  # last part of outer split
$$ LANGUAGE 'plperl' IMMUTABLE STRICT;

-- Reverse Domain
CREATE OR REPLACE FUNCTION email_reverse_domain(email) RETURNS text AS
$$
   return join(".", reverse split(   # Split the output of split below
        /\./,  # Split the following on "."
        (
            split(
                /\@/,  # Split whole input on "@"
                $_[0]
            )
        )[-1]  # last part of inner split
    ));
$$ LANGUAGE 'plperl' IMMUTABLE STRICT;

-- Nonsense function to ensure func select will choose this one
CREATE OR REPLACE FUNCTION email_n(email, integer) returns text as
$$
	select $1::text || '-' || $2::numeric
$$ LANGUAGE 'SQL' IMMUTABLE STRICT;
create operator * ( PROCEDURE= email_n, LEFTARG=email, RIGHTARG=integer);
