--
-- CREATE_OPERATOR
--
CREATE OPERATOR ## (
    LEFTARG = path,
    RIGHTARG = path,
    FUNCTION = path_inter,
    commutator = ##
);

CREATE OPERATOR <% (
    LEFTARG = point,
    RIGHTARG = widget,
    PROCEDURE = pt_in_widget,
    commutator = >%,
    negator = >=%
);

CREATE OPERATOR @#@ (
    RIGHTARG = int8, -- left unary
    PROCEDURE = numeric_fac
);

CREATE OPERATOR #@# (
    LEFTARG = int8, -- right unary
    PROCEDURE = numeric_fac
);

CREATE OPERATOR #%# (
    LEFTARG = int8, -- right unary
    PROCEDURE = numeric_fac
);

-- Test operator created above
SELECT
    point '(1,2)' <% widget '(0,0,3)' AS t,
    point '(1,2)' <% widget '(0,0,1)' AS f;

-- Test comments
COMMENT ON OPERATOR ## ## ## (int4,
    NONE) IS 'bad right unary';

-- => is disallowed now
CREATE OPERATOR => (
    LEFTARG = int8, -- right unary
    PROCEDURE = numeric_fac
);

-- lexing of <=, >=, <>, != has a number of edge cases
-- (=> is tested elsewhere)
-- this is legal because ! is not allowed in sql ops
CREATE OPERATOR !=- (
    LEFTARG = int8, -- right unary
    PROCEDURE = numeric_fac
);

SELECT
    2 != -;

-- make sure lexer returns != as <> even in edge cases
SELECT
    2 !=
    /**/
    1,
    2 !=
    /**/
    2;

SELECT
    2 != -- comment to be removed by psql 1;

DO $$
    -- use DO to protect -- from psql
DECLARE
    r boolean;
BEGIN
    EXECUTE $e$ select 2 !=-- comment
      1 $e$ INTO r;
    RAISE info 'r = %', r;
END;
$$;

-- check that <= etc. followed by more operator characters are returned
-- as the correct token with correct precedence
SELECT
    TRUE <> - 1 BETWEEN 1 AND 1;

-- BETWEEN has prec. above <> but below Op
SELECT
    FALSE <>
    /**/
    1 BETWEEN 1 AND 1;

SELECT
    FALSE <= - 1 BETWEEN 1 AND 1;

SELECT
    FALSE >= - 1 BETWEEN 1 AND 1;

SELECT
    2 <=
    /**/
    3,
    3 >=
    /**/
    2,
    2 <>
    /**/
    3;

SELECT
    3 <=
    /**/
    2,
    2 >=
    /**/
    3,
    2 <>
    /**/
    2;

-- Should fail. CREATE OPERATOR requires USAGE on SCHEMA
BEGIN TRANSACTION;
CREATE ROLE regress_rol_op1;
CREATE SCHEMA schema_op1;
GRANT USAGE ON SCHEMA schema_op1 TO PUBLIC;
REVOKE USAGE ON SCHEMA schema_op1 FROM regress_rol_op1;
SET ROLE regress_rol_op1;
CREATE OPERATOR schema_op1. # * # (
    LEFTARG = int8, -- right unary
    PROCEDURE = numeric_fac
);
ROLLBACK;

-- Should fail. SETOF type functions not allowed as argument (testing leftarg)
BEGIN TRANSACTION;
CREATE OPERATOR "#*#" (
    LEFTARG = SETOF int8,
    PROCEDURE = numeric_fac
);
ROLLBACK;

-- Should fail. SETOF type functions not allowed as argument (testing rightarg)
BEGIN TRANSACTION;
CREATE OPERATOR "#*#" (
    RIGHTARG = SETOF int8,
    PROCEDURE = numeric_fac
);
ROLLBACK;

-- Should work. Sample text-book case
BEGIN TRANSACTION;
CREATE OR REPLACE FUNCTION fn_op2 (boolean, boolean)
    RETURNS boolean
    AS $$
    SELECT
        NULL::boolean;
$$
LANGUAGE sql
IMMUTABLE;
CREATE OPERATOR === (
    LEFTARG = boolean,
    RIGHTARG = boolean,
    PROCEDURE = fn_op2,
    COMMUTATOR = ===,
    NEGATOR = !==,
    RESTRICT = contsel,
    JOIN = contjoinsel,
    SORT1,
    SORT2,
    LTCMP,
    GTCMP,
    HASHES,
    MERGES
);
ROLLBACK;

-- Should fail. Invalid attribute
CREATE OPERATOR #@%# (
    LEFTARG = int8, -- right unary
    PROCEDURE = numeric_fac,
    invalid_att = int8
);

-- Should fail. At least leftarg or rightarg should be mandatorily specified
CREATE OPERATOR #@%# (
    PROCEDURE = numeric_fac
);

-- Should fail. Procedure should be mandatorily specified
CREATE OPERATOR #@%# (
    LEFTARG = int8
);

-- Should fail. CREATE OPERATOR requires USAGE on TYPE
BEGIN TRANSACTION;
CREATE ROLE regress_rol_op3;
CREATE TYPE type_op3 AS ENUM (
    'new',
    'open',
    'closed'
);
CREATE FUNCTION fn_op3 (type_op3, int8)
    RETURNS int8
    AS $$
    SELECT
        NULL::int8;
$$
LANGUAGE sql
IMMUTABLE;
REVOKE USAGE ON TYPE type_op3 FROM regress_rol_op3;
REVOKE USAGE ON TYPE type_op3 FROM PUBLIC;
-- Need to do this so that regress_rol_op3 is not allowed USAGE via PUBLIC
SET ROLE regress_rol_op3;
CREATE OPERATOR "#*#" (
    LEFTARG = type_op3,
    RIGHTARG = int8,
    PROCEDURE = fn_op3
);
ROLLBACK;

-- Should fail. CREATE OPERATOR requires USAGE on TYPE (need to check separately for rightarg)
BEGIN TRANSACTION;
CREATE ROLE regress_rol_op4;
CREATE TYPE type_op4 AS ENUM (
    'new',
    'open',
    'closed'
);
CREATE FUNCTION fn_op4 (int8, type_op4)
    RETURNS int8
    AS $$
    SELECT
        NULL::int8;
$$
LANGUAGE sql
IMMUTABLE;
REVOKE USAGE ON TYPE type_op4 FROM regress_rol_op4;
REVOKE USAGE ON TYPE type_op4 FROM PUBLIC;
-- Need to do this so that regress_rol_op3 is not allowed USAGE via PUBLIC
SET ROLE regress_rol_op4;
CREATE OPERATOR "#*#" (
    LEFTARG = int8,
    RIGHTARG = type_op4,
    PROCEDURE = fn_op4
);
ROLLBACK;

-- Should fail. CREATE OPERATOR requires EXECUTE on function
BEGIN TRANSACTION;
CREATE ROLE regress_rol_op5;
CREATE TYPE type_op5 AS ENUM (
    'new',
    'open',
    'closed'
);
CREATE FUNCTION fn_op5 (int8, int8)
    RETURNS int8
    AS $$
    SELECT
        NULL::int8;
$$
LANGUAGE sql
IMMUTABLE;
REVOKE EXECUTE ON FUNCTION fn_op5 (int8, int8) FROM regress_rol_op5;
REVOKE EXECUTE ON FUNCTION fn_op5 (int8, int8) FROM PUBLIC;
-- Need to do this so that regress_rol_op3 is not allowed EXECUTE via PUBLIC SET ROLE regress_rol_op5;
CREATE OPERATOR "#*#" (
    LEFTARG = int8,
    RIGHTARG = int8,
    PROCEDURE = fn_op5
);
ROLLBACK;

-- Should fail. CREATE OPERATOR requires USAGE on return TYPE
BEGIN TRANSACTION;
CREATE ROLE regress_rol_op6;
CREATE TYPE type_op6 AS ENUM (
    'new',
    'open',
    'closed'
);
CREATE FUNCTION fn_op6 (int8, int8)
    RETURNS type_op6
    AS $$
    SELECT
        NULL::type_op6;
$$
LANGUAGE sql
IMMUTABLE;
REVOKE USAGE ON TYPE type_op6 FROM regress_rol_op6;
REVOKE USAGE ON TYPE type_op6 FROM PUBLIC;
-- Need to do this so that regress_rol_op3 is not allowed USAGE via PUBLIC
SET ROLE regress_rol_op6;
CREATE OPERATOR "#*#" (
    LEFTARG = int8,
    RIGHTARG = int8,
    PROCEDURE = fn_op6
);
ROLLBACK;

-- invalid: non-lowercase quoted identifiers
CREATE OPERATOR === (
    "Leftarg" = box,
    "Rightarg" = box,
    "Procedure" = area_equal_function,
    "Commutator" = == =,
    "Negator" = != =,
    "Restrict" = area_restriction_function,
    "Join" = area_join_function,
    "Hashes",
    "Merges"
);

