text: -- This file and its contents are licensed under the Apache License 2.0.
  -- Please see the included NOTICE for copyright information and
  -- LICENSE-APACHE for a copy of the license.
  
  \c :TEST_DBNAME :ROLE_SUPERUSER
  CREATE OR REPLACE FUNCTION _timescaledb_internal.test_status(int) RETURNS JSONB
      AS :MODULE_PATHNAME, 'ts_test_status' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
  CREATE OR REPLACE FUNCTION _timescaledb_internal.test_status_ssl(int) RETURNS JSONB
      AS :MODULE_PATHNAME, 'ts_test_status_ssl' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
  CREATE OR REPLACE FUNCTION _timescaledb_internal.test_status_mock(text) RETURNS JSONB
      AS :MODULE_PATHNAME, 'ts_test_status_mock' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
  CREATE OR REPLACE FUNCTION _timescaledb_internal.test_validate_server_version(response text)
      RETURNS TEXT
      AS :MODULE_PATHNAME, 'ts_test_validate_server_version' LANGUAGE C IMMUTABLE PARALLEL SAFE;
  CREATE OR REPLACE FUNCTION _timescaledb_internal.test_telemetry_main_conn(text, text)
  RETURNS BOOLEAN AS :MODULE_PATHNAME, 'ts_test_telemetry_main_conn' LANGUAGE C IMMUTABLE PARALLEL SAFE;
  CREATE OR REPLACE FUNCTION _timescaledb_internal.test_telemetry(host text = NULL, servname text = NULL, port int = NULL) RETURNS JSONB AS :MODULE_PATHNAME, 'ts_test_telemetry' LANGUAGE C IMMUTABLE PARALLEL SAFE;
  CREATE OR REPLACE FUNCTION _timescaledb_internal.test_privacy() RETURNS BOOLEAN
      AS :MODULE_PATHNAME, 'ts_test_privacy' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
  
  CREATE OR REPLACE FUNCTION test_check_version_response(response text)
         RETURNS VOID
         AS :MODULE_PATHNAME, 'ts_test_check_version_response'
         LANGUAGE C
         IMMUTABLE STRICT PARALLEL SAFE;
  
  INSERT INTO _timescaledb_catalog.metadata VALUES ('foo','bar',TRUE);
  INSERT INTO _timescaledb_catalog.metadata VALUES ('bar','baz',FALSE);
  
  \c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
  SELECT _timescaledb_internal.test_status_ssl(200);
  SELECT _timescaledb_internal.test_status_ssl(201);
  \set ON_ERROR_STOP 0
  SELECT _timescaledb_internal.test_status_ssl(304);
  SELECT _timescaledb_internal.test_status_ssl(400);
  SELECT _timescaledb_internal.test_status_ssl(401);
  SELECT _timescaledb_internal.test_status_ssl(404);
  SELECT _timescaledb_internal.test_status_ssl(500);
  SELECT _timescaledb_internal.test_status_ssl(503);
  \set ON_ERROR_STOP 1
  SELECT _timescaledb_internal.test_status(200);
  SELECT _timescaledb_internal.test_status(201);
  \set ON_ERROR_STOP 0
  SELECT _timescaledb_internal.test_status(304);
  SELECT _timescaledb_internal.test_status(400);
  SELECT _timescaledb_internal.test_status(401);
  SELECT _timescaledb_internal.test_status(404);
  SELECT _timescaledb_internal.test_status(500);
  SELECT _timescaledb_internal.test_status(503);
  \set ON_ERROR_STOP 1
  
  -- This function runs the test 5 times, because each time the internal C function is choosing a random length to send from the server on each socket read. We hit many cases this way.
  CREATE OR REPLACE FUNCTION mocker(TEXT) RETURNS SETOF TEXT AS
  $BODY$
  DECLARE
  r TEXT;
  BEGIN
  FOR i in 1..5 LOOP
  SELECT _timescaledb_internal.test_status_mock($1) INTO r;
  RETURN NEXT r;
  END LOOP;
  RETURN;
  END
  $BODY$
  LANGUAGE 'plpgsql';
  
  select * from mocker(
          E'HTTP/1.1 200 OK\r\n'
          'Content-Type: application/json; charset=utf-8\r\n'
          'Date: Thu, 12 Jul 2018 18:33:04 GMT\r\n'
          'ETag: W/\"e-upYEWCL+q6R/++2nWHz5b76hBgo\"\r\n'
          'Server: nginx\r\n'
          'Vary: Accept-Encoding\r\n'
          'Content-Length: 14\r\n'
          'Connection: Close\r\n\r\n'
          '{\"status\":200}');
  select * from mocker(
          E'HTTP/1.1 201 OK\r\n'
          'Content-Type: application/json; charset=utf-8\r\n'
          'Vary: Accept-Encoding\r\n'
          'Content-Length: 14\r\n'
          'Connection: Close\r\n\r\n'
          '{\"status\":201}');
  
  \set ON_ERROR_STOP 0
  \set test_string 'HTTP/1.1 404 Not Found\r\nContent-Length: 14\r\nConnection: Close\r\n\r\n{\"status\":404}';
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  \set test_string 'Content-Length: 14\r\nConnection: Close\r\n\r\n{\"status\":404}';
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  \set test_string 'HTTP/1.1 404 Not Found\r\nContent-Length: 14\r\nConnection: Close\r\n{\"status\":404}';
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  SELECT _timescaledb_internal.test_status_mock(:'test_string');
  \set ON_ERROR_STOP 1
  
  -- Test parsing version response
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"status": "200", "current_timescaledb_version": "10.1.0"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "10.1"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "10"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "9.2.0"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "9.1.2"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "1.0.0"}');
  
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "1.0.0-rc1"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "1.0.0-rc2"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "1.0.0-rc1"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "1.0.0-alpha"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "123456789"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "!@#$%"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": ""}');
  
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": " 10 "}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "a"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "a.b.c"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "10.1.1a"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "10.1.1+rc1"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "10.1.1.1"}');
  SELECT * FROM _timescaledb_internal.test_validate_server_version('{"current_timescaledb_version": "1.0.0-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"}');
  
  
  ----------------------------------------------------------------
  -- Test well-formed response and valid versions
  SELECT test_check_version_response('{"current_timescaledb_version": "1.6.1", "is_up_to_date": true}');
  SELECT test_check_version_response('{"current_timescaledb_version": "1.6.1", "is_up_to_date": false}');
  SELECT test_check_version_response('{"current_timescaledb_version": "10.1", "is_up_to_date": false}');
  SELECT test_check_version_response('{"current_timescaledb_version": "10.1.1-rc1", "is_up_to_date": false}');
  
  ----------------------------------------------------------------
  -- Test well-formed response but invalid versions
  SELECT test_check_version_response('{"current_timescaledb_version": "1.0.0-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "is_up_to_date": false}');
  SELECT test_check_version_response('{"current_timescaledb_version": "10.1.1+rc1", "is_up_to_date": false}');
  SELECT test_check_version_response('{"current_timescaledb_version": "@10.1.1", "is_up_to_date": false}');
  SELECT test_check_version_response('{"current_timescaledb_version": "10.1.1@", "is_up_to_date": false}');
  
  ----------------------------------------------------------------
  -- Test malformed responses
  
  \set ON_ERROR_STOP 0
  -- Empty response
  SELECT test_check_version_response('{}');
  
  -- Field "is_up_to_date" missing
  SELECT test_check_version_response('{"current_timescaledb_version": "1.6.1"}');
  
  -- Field "current_timescaledb_version" is missing
  SELECT test_check_version_response('{"is_up_to_date": false}');
  \set ON_ERROR_STOP 1
  
  SET timescaledb.telemetry_level=basic;
  -- Connect to a bogus host and path to test error handling in telemetry_main()
  SELECT _timescaledb_internal.test_telemetry_main_conn('noservice.timescale.com', 'path');
  
  -- Test telemetry report contents
  SET timescaledb.telemetry_level=basic;
  
  SELECT * FROM jsonb_object_keys(get_telemetry_report()) AS key
  WHERE key != 'os_name_pretty';
  
  CREATE MATERIALIZED VIEW telemetry_report AS
  SELECT t FROM get_telemetry_report() t;
  
  -- check telemetry picks up flagged content from metadata
  SELECT t -> 'db_metadata' FROM telemetry_report;
  
  -- check timescaledb_telemetry.cloud
  SELECT t -> 'instance_metadata' FROM telemetry_report;
  
  WITH t AS (
  	 SELECT t -> 'relations' AS rels
  	 FROM telemetry_report
  )
  SELECT rels -> 'hypertables' -> 'num_relations' AS num_hypertables,
  	   rels -> 'continuous_aggregates' -> 'num_relations' AS num_caggs
  FROM t;
  
  CREATE TABLE device_readings (
        observation_time  TIMESTAMPTZ       NOT NULL
  );
  SELECT table_name FROM create_hypertable('device_readings', 'observation_time');
  
  REFRESH MATERIALIZED VIEW telemetry_report;
  
  WITH t AS (
  	 SELECT t -> 'relations' AS rels
  	 FROM telemetry_report
  )
  SELECT rels -> 'hypertables' -> 'num_relations' AS num_hypertables,
  	   rels -> 'continuous_aggregates' -> 'num_relations' AS num_caggs
  FROM t;
  
  set datestyle to iso;
  -- check that installed_time formatting in telemetry report does not depend on local date settings
  SELECT t -> 'installed_time' AS installed_time FROM telemetry_report
  \gset
  set datestyle to sql;
  SELECT t-> 'installed_time' AS installed_time2 FROM telemetry_report
  \gset
  
  SELECT :'installed_time' = :'installed_time2' AS equal, length(:'installed_time'), length(:'installed_time2');
  
  RESET datestyle;
  
  -- test function call telemetry
  CREATE FUNCTION not_visible_in_telemetry() RETURNS INT AS $$
      SELECT 1;
  $$ LANGUAGE SQL;
  
  -- drain old function call telemetry so we have fixed out put;
  SELECT FROM get_telemetry_report();
  
  -- call some arbirary functions
  SELECT 1 + 1, not_visible_in_telemetry(), 1 + 1, abs(-1), not_visible_in_telemetry()
  WHERE 1 + 1 = 2;
  
  -- call some aggregates
  SELECT min(not_visible_in_telemetry()), sum(not_visible_in_telemetry());
  
  -- check that we can record from a prepared statement
  PREPARE record_from_prepared AS SELECT 1 - 1;
  
  -- execute 10 times to make sure turning it into generic plan works
  EXECUTE record_from_prepared;
  EXECUTE record_from_prepared;
  EXECUTE record_from_prepared;
  EXECUTE record_from_prepared;
  EXECUTE record_from_prepared;
  EXECUTE record_from_prepared;
  EXECUTE record_from_prepared;
  EXECUTE record_from_prepared;
  EXECUTE record_from_prepared;
  EXECUTE record_from_prepared;
  
  DEALLOCATE record_from_prepared;
  
  SELECT get_telemetry_report()->'functions_used';
  
  -- check the report again to see if resetting works
  SELECT get_telemetry_report()->'functions_used';
  
  \c :TEST_DBNAME :ROLE_SUPERUSER
  TRUNCATE _timescaledb_catalog.metadata;
  
  SET timescaledb.telemetry_level=off;
  -- returns false which means telemetry got canceled
  SELECT * FROM _timescaledb_internal.test_privacy();
  
  RESET timescaledb.telemetry_level;
  -- returns false which means telemetry got canceled
  SELECT * FROM _timescaledb_internal.test_privacy();
  
  -- To make sure nothing was sent, we check the UUID table to make sure no exported UUID row was created
  SELECT key from _timescaledb_catalog.metadata;
  
  \set ON_ERROR_STOP 0
  -- test that the telemetry gathering code doesn't break nonexistent statements
  EXECUTE noexistent_statement;
  