-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
\c :TEST_DBNAME :ROLE_SUPERUSER
-- We have support for a dedicated type whos only purpose is to
-- generate error. You pass in the error code you want and it will be
-- raised from inside the input function.
SET client_min_messages TO WARNING;
CREATE TYPE sqlstate_raise;
CREATE FUNCTION ts_sqlstate_raise_in(cstring)
    RETURNS sqlstate_raise
    AS :MODULE_PATHNAME LANGUAGE C STABLE STRICT;
CREATE FUNCTION ts_sqlstate_raise_out(sqlstate_raise)
    RETURNS cstring
    AS :MODULE_PATHNAME LANGUAGE C STABLE STRICT;
CREATE TYPE sqlstate_raise (
    input = ts_sqlstate_raise_in,
    output = ts_sqlstate_raise_out
);
SET client_min_messages TO DEFAULT;
CREATE OR REPLACE FUNCTION test_with_clause_filter(with_clauses TEXT[][])
    RETURNS TABLE(namespace TEXT, name TEXT, value TEXT, filtered BOOLEAN)
    AS :MODULE_PATHNAME, 'ts_test_with_clause_filter' LANGUAGE C VOLATILE STRICT;
CREATE OR REPLACE FUNCTION test_with_clause_parse(with_clauses TEXT[][])
    RETURNS TABLE(name TEXT, unimpl INT8, bool BOOLEAN, int32 INT4, def INT4, name_arg NAME, regc REGCLASS)
    AS :MODULE_PATHNAME, 'ts_test_with_clause_parse' LANGUAGE C VOLATILE STRICT;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
SELECT * FROM test_with_clause_filter(
        '{
            {"baz", "bar", "foo"},
            {"timescaledb", "bar", "baz"},
            {"bar", "timescaledb", "baz"},
            {"timescaledb", "baz", "bar"},
            {"tsdb", "qux", "bar"},
            {"tsdb", "quux", "bar"}
        }');
  namespace  |    name     | value | filtered 
-------------+-------------+-------+----------
 timescaledb | bar         | baz   | t
 timescaledb | baz         | bar   | t
 tsdb        | qux         | bar   | t
 tsdb        | quux        | bar   | t
 baz         | bar         | foo   | t
 bar         | timescaledb | baz   | t

SELECT * FROM test_with_clause_filter(
        '{
            {"baz", "bar", "foo"},
            {"bar", "timescaledb", "baz"},
            {"bar", "timescaledb", "baz"}
        }');
 namespace |    name     | value | filtered 
-----------+-------------+-------+----------
 baz       | bar         | foo   | t
 bar       | timescaledb | baz   | t
 bar       | timescaledb | baz   | t

SELECT * FROM test_with_clause_filter(
        '{
            {"bar", "timescaledb"},
            {"baz", "bar"},
            {"timescaledb", "bar"},
            {"timescaledb", "baz"}
        }');
  namespace  |    name     | value | filtered 
-------------+-------------+-------+----------
 timescaledb | bar         |       | t
 timescaledb | baz         |       | t
 bar         | timescaledb |       | t
 baz         | bar         |       | t

SELECT * FROM test_with_clause_filter(
        '{
            {"timescaledb"},
            {"bar"},
            {"baz"}
        }');
 namespace |    name     | value | filtered 
-----------+-------------+-------+----------
           | timescaledb |       | f
           | bar         |       | f
           | baz         |       | f

\set ON_ERROR_STOP 0
-- unrecognized argument
SELECT * FROM test_with_clause_parse('{{"timescaledb", "fakearg", "bar"}}');
ERROR:  unrecognized parameter "timescaledb.fakearg"
SELECT * FROM test_with_clause_parse('{{"timescaledb", "fakearg"}}');
ERROR:  unrecognized parameter "timescaledb.fakearg"
-- unimplemented handled gracefully
SELECT * FROM test_with_clause_parse('{{"timescaledb", "unimplemented", "bar"}}');
ERROR:  argument "timescaledb.unimplemented" not implemented
SELECT * FROM test_with_clause_parse('{{"timescaledb", "unimplemented", "true"}}');
ERROR:  argument "timescaledb.unimplemented" not implemented
SELECT * FROM test_with_clause_parse('{{"timescaledb", "unimplemented"}}');
ERROR:  argument "timescaledb.unimplemented" not implemented
\set ON_ERROR_STOP 1
-- bool parsing
SELECT * FROM test_with_clause_parse('{{"timescaledb", "bool", "true"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        | t    |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "bool", "false"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        | f    |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "bool", "on"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        | t    |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "bool", "off"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        | f    |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "bool", "1"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        | t    |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "bool", "0"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        | f    |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "bool"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        | t    |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

-- int32 parsing
SELECT * FROM test_with_clause_parse('{{"timescaledb", "int32", "1"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |     1 |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "int32", "572"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |   572 |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "int32", "-10"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |   -10 |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

\set ON_ERROR_STOP 0
SELECT * FROM test_with_clause_parse('{{"timescaledb", "int32", "true"}}');
ERROR:  invalid value for timescaledb.int32 'true'
SELECT * FROM test_with_clause_parse('{{"timescaledb", "int32", "bar"}}');
ERROR:  invalid value for timescaledb.int32 'bar'
SELECT * FROM test_with_clause_parse('{{"timescaledb", "int32"}}');
ERROR:  parameter "timescaledb.int32" must have a value
\set ON_ERROR_STOP 1
-- name parsing
SELECT * FROM test_with_clause_parse('{{"timescaledb", "name", "1"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      | 1        | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "name", "572"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      | 572      | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "name", "-10"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      | -10      | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "name", "true"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      | true     | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "name", "bar"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      | bar      | 
 regclass       |        |      |       |      |          | 
 sqlstate_raise |        |      |       |      |          | 

\set ON_ERROR_STOP 0
SELECT * FROM test_with_clause_parse('{{"timescaledb", "name"}}');
ERROR:  parameter "timescaledb.name" must have a value
\set ON_ERROR_STOP 1
-- REGCLASS parsing
SELECT * FROM test_with_clause_parse('{{"timescaledb", "regclass", "pg_type"}}');
      name      | unimpl | bool | int32 | def  | name_arg |  regc   
----------------+--------+------+-------+------+----------+---------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | pg_type
 sqlstate_raise |        |      |       |      |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "regclass", "1"}}');
      name      | unimpl | bool | int32 | def  | name_arg | regc 
----------------+--------+------+-------+------+----------+------
 unimplemented  |        |      |       |      |          | 
 bool           |        |      |       |      |          | 
 int32          |        |      |       |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      |          | 
 regclass       |        |      |       |      |          | 1
 sqlstate_raise |        |      |       |      |          | 

\set ON_ERROR_STOP 0
SELECT * FROM test_with_clause_parse('{{"timescaledb", "regclass", "-10"}}');
ERROR:  invalid value for timescaledb.regclass '-10'
SELECT * FROM test_with_clause_parse('{{"timescaledb", "regclass", "true"}}');
ERROR:  invalid value for timescaledb.regclass 'true'
SELECT * FROM test_with_clause_parse('{{"timescaledb", "regclass", "bar"}}');
ERROR:  invalid value for timescaledb.regclass 'bar'
SELECT * FROM test_with_clause_parse('{{"timescaledb", "regclass"}}');
ERROR:  parameter "timescaledb.regclass" must have a value
\set ON_ERROR_STOP 1
-- Test errors generated from inside the input function
\set ON_ERROR_STOP 0
-- Out of memory, "hard" error.
SELECT * FROM test_with_clause_parse('{{"timescaledb", "sqlstate_raise", "53200"}}');
ERROR:  raised requested error code "53200"
-- Division by zero, "soft" error. Shows invalid value message (with a
-- strange message in this case).
SELECT * FROM test_with_clause_parse('{{"timescaledb", "sqlstate_raise", "22012"}}');
ERROR:  invalid value for timescaledb.sqlstate_raise '22012'
\set ON_ERROR_STOP 1
-- defaults get overridden
SELECT * FROM test_with_clause_parse('{{"timescaledb", "default", "1"}}');
      name      | unimpl | bool | int32 | def | name_arg | regc 
----------------+--------+------+-------+-----+----------+------
 unimplemented  |        |      |       |     |          | 
 bool           |        |      |       |     |          | 
 int32          |        |      |       |     |          | 
 default        |        |      |       |   1 |          | 
 name           |        |      |       |     |          | 
 regclass       |        |      |       |     |          | 
 sqlstate_raise |        |      |       |     |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "default", "572"}}');
      name      | unimpl | bool | int32 | def | name_arg | regc 
----------------+--------+------+-------+-----+----------+------
 unimplemented  |        |      |       |     |          | 
 bool           |        |      |       |     |          | 
 int32          |        |      |       |     |          | 
 default        |        |      |       | 572 |          | 
 name           |        |      |       |     |          | 
 regclass       |        |      |       |     |          | 
 sqlstate_raise |        |      |       |     |          | 

SELECT * FROM test_with_clause_parse('{{"timescaledb", "default", "-10"}}');
      name      | unimpl | bool | int32 | def | name_arg | regc 
----------------+--------+------+-------+-----+----------+------
 unimplemented  |        |      |       |     |          | 
 bool           |        |      |       |     |          | 
 int32          |        |      |       |     |          | 
 default        |        |      |       | -10 |          | 
 name           |        |      |       |     |          | 
 regclass       |        |      |       |     |          | 
 sqlstate_raise |        |      |       |     |          | 

\set ON_ERROR_STOP 0
SELECT * FROM test_with_clause_parse('{{"timescaledb", "default", "true"}}');
ERROR:  invalid value for timescaledb.default 'true'
SELECT * FROM test_with_clause_parse('{{"timescaledb", "default", "bar"}}');
ERROR:  invalid value for timescaledb.default 'bar'
SELECT * FROM test_with_clause_parse('{{"timescaledb", "default"}}');
ERROR:  parameter "timescaledb.default" must have a value
\set ON_ERROR_STOP 1
\set ON_ERROR_STOP 0
-- duplicates error
SELECT * FROM test_with_clause_parse('{{"timescaledb", "name", "1"}, {"timescaledb", "name", "572"}}');
ERROR:  duplicate parameter "timescaledb.name"
\set ON_ERROR_STOP 1
-- multiple args
SELECT * FROM test_with_clause_parse('{
    {"a", "bool", "true"},
    {"b", "int32", "572"},
    {"c", "name", "bar"},
    {"d", "regclass", "pg_type"}
}');
      name      | unimpl | bool | int32 | def  | name_arg |  regc   
----------------+--------+------+-------+------+----------+---------
 unimplemented  |        |      |       |      |          | 
 bool           |        | t    |       |      |          | 
 int32          |        |      |   572 |      |          | 
 default        |        |      |       | -100 |          | 
 name           |        |      |       |      | bar      | 
 regclass       |        |      |       |      |          | pg_type
 sqlstate_raise |        |      |       |      |          | 

\c :TEST_DBNAME :ROLE_SUPERUSER
DROP TYPE sqlstate_raise CASCADE;
NOTICE:  drop cascades to 2 other objects
