--
-- MULTI_EXTENSION
--
-- Tests around extension creation / upgrades
--
-- It'd be nice to script generation of this file, but alas, that's
-- not done yet.
--
-- Upgrade tests verify the objects are added in citus_finish_pg_upgrade()
SET citus.next_shard_id TO 580000;
CREATE SCHEMA multi_extension;
SELECT $definition$
CREATE OR REPLACE FUNCTION test.maintenance_worker()
    RETURNS pg_stat_activity
    LANGUAGE plpgsql
AS $$
DECLARE
   activity record;
BEGIN
    DO 'BEGIN END'; -- Force maintenance daemon to start
    -- we don't want to wait forever; loop will exit after 20 seconds
    FOR i IN 1 .. 200 LOOP
        PERFORM pg_stat_clear_snapshot();
        SELECT * INTO activity FROM pg_stat_activity
        WHERE application_name = 'Citus Maintenance Daemon' AND datname = current_database();
        IF activity.pid IS NOT NULL THEN
            RETURN activity;
        ELSE
            PERFORM pg_sleep(0.1);
        END IF ;
    END LOOP;
    -- fail if we reach the end of this loop
    raise 'Waited too long for maintenance daemon to start';
END;
$$;
$definition$ create_function_test_maintenance_worker
\gset
CREATE TABLE multi_extension.prev_objects(description text);
CREATE TABLE multi_extension.extension_diff(previous_object text COLLATE "C",
                            current_object text COLLATE "C");
-- In PG17, Auto-generated array types, multirange types, and relation rowtypes
-- are treated as dependent objects, hence changing the output of the
-- print_extension_changes function.
-- Relevant PG commit: e5bc9454e527b1cba97553531d8d4992892fdeef
-- Here we create a table with only the basic extension types
-- in order to avoid printing extra ones for now
-- This can be removed when we drop PG16 support.
CREATE TABLE multi_extension.extension_basic_types (description text);
INSERT INTO multi_extension.extension_basic_types VALUES ('type citus.distribution_type'),
                                                         ('type citus.shard_transfer_mode'),
                                                         ('type citus_copy_format'),
                                                         ('type noderole'),
                                                         ('type citus_job_status'),
                                                         ('type citus_task_status'),
                                                         ('type replication_slot_info'),
                                                         ('type split_copy_info'),
                                                         ('type split_shard_info'),
                                                         ('type cluster_clock');
CREATE FUNCTION multi_extension.print_extension_changes()
RETURNS TABLE(previous_object text, current_object text)
AS $func$
BEGIN
    SET LOCAL search_path TO multi_extension;
	TRUNCATE TABLE extension_diff;

	CREATE TABLE current_objects AS
	SELECT pg_catalog.pg_describe_object(classid, objid, 0)
           || ' ' ||
           coalesce(pg_catalog.pg_get_function_result(objid), '') AS description
	FROM pg_catalog.pg_depend, pg_catalog.pg_extension e
	WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass
		AND refobjid = e.oid
		AND deptype = 'e'
		AND e.extname='citus'
        AND (pg_catalog.pg_describe_object(classid, objid, 0) NOT LIKE 'type%'
             OR
             pg_catalog.pg_describe_object(classid, objid, 0) IN (SELECT * FROM extension_basic_types));

	INSERT INTO extension_diff
	SELECT p.description previous_object, c.description current_object
	FROM current_objects c FULL JOIN prev_objects p
	ON p.description = c.description
	WHERE (p.description is null OR c.description is null)
          AND c.description IS DISTINCT FROM 'function any_value(anyelement) anyelement'
          AND c.description IS DISTINCT FROM 'function any_value_agg(anyelement,anyelement) anyelement';

	DROP TABLE prev_objects;
	ALTER TABLE current_objects RENAME TO prev_objects;

	RETURN QUERY SELECT * FROM extension_diff ORDER BY 1, 2;
END
$func$ LANGUAGE plpgsql;
CREATE SCHEMA test;
:create_function_test_maintenance_worker
-- check maintenance daemon is started
SELECT datname, current_database(),
    usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus')
FROM test.maintenance_worker();
  datname   | current_database | usename  | extowner
---------------------------------------------------------------------
 regression | regression       | postgres | postgres
(1 row)

-- ensure no unexpected objects were created outside pg_catalog
SELECT pgio.type, pgio.identity
FROM pg_depend AS pgd,
	 pg_extension AS pge,
	 LATERAL pg_identify_object(pgd.classid, pgd.objid, pgd.objsubid) AS pgio
WHERE pgd.refclassid = 'pg_extension'::regclass AND
	  pgd.refobjid   = pge.oid AND
	  pge.extname    = 'citus' AND
	  pgio.schema    NOT IN ('pg_catalog', 'citus', 'citus_internal', 'test', 'columnar', 'columnar_internal') AND
      pgio.type      != 'type'
ORDER BY 1, 2;
 type |       identity
---------------------------------------------------------------------
 view | public.citus_schemas
 view | public.citus_tables
(2 rows)

-- DROP EXTENSION pre-created by the regression suite
DROP EXTENSION citus;
DROP EXTENSION citus_columnar;
\c
-- these tests switch between citus versions and call ddl's that require pg_dist_object to be created
SET citus.enable_metadata_sync TO 'false';
SET citus.enable_version_checks TO 'false';
SET columnar.enable_version_checks TO 'false';
CREATE EXTENSION citus VERSION '8.0-1';
ALTER EXTENSION citus UPDATE TO '8.0-2';
ALTER EXTENSION citus UPDATE TO '8.0-3';
ALTER EXTENSION citus UPDATE TO '8.0-4';
ALTER EXTENSION citus UPDATE TO '8.0-5';
ALTER EXTENSION citus UPDATE TO '8.0-6';
ALTER EXTENSION citus UPDATE TO '8.0-7';
ALTER EXTENSION citus UPDATE TO '8.0-8';
ALTER EXTENSION citus UPDATE TO '8.0-9';
ALTER EXTENSION citus UPDATE TO '8.0-10';
ALTER EXTENSION citus UPDATE TO '8.0-11';
ALTER EXTENSION citus UPDATE TO '8.0-12';
ALTER EXTENSION citus UPDATE TO '8.0-13';
ALTER EXTENSION citus UPDATE TO '8.1-1';
ALTER EXTENSION citus UPDATE TO '8.2-1';
ALTER EXTENSION citus UPDATE TO '8.2-2';
ALTER EXTENSION citus UPDATE TO '8.2-3';
ALTER EXTENSION citus UPDATE TO '8.2-4';
ALTER EXTENSION citus UPDATE TO '8.3-1';
ALTER EXTENSION citus UPDATE TO '9.0-1';
ALTER EXTENSION citus UPDATE TO '9.0-2';
ALTER EXTENSION citus UPDATE TO '9.1-1';
ALTER EXTENSION citus UPDATE TO '9.2-1';
ALTER EXTENSION citus UPDATE TO '9.2-2';
-- Snapshot of state at 9.2-2
SELECT * FROM multi_extension.print_extension_changes();
 previous_object |                                                                                                        current_object
---------------------------------------------------------------------
                 | event trigger citus_cascade_to_partition
                 | function alter_role_if_exists(text,text) boolean
                 | function array_cat_agg(anycompatiblearray) anycompatiblearray
                 | function assign_distributed_transaction_id(integer,bigint,timestamp with time zone) void
                 | function authinfo_valid(text) boolean
                 | function broadcast_intermediate_result(text,text) bigint
                 | function check_distributed_deadlocks() boolean
                 | function citus_add_rebalance_strategy(name,regproc,regproc,regproc,real,real) void
                 | function citus_blocking_pids(integer) integer[]
                 | function citus_create_restore_point(text) pg_lsn
                 | function citus_dist_stat_activity() SETOF record
                 | function citus_drop_trigger() event_trigger
                 | function citus_executor_name(integer) text
                 | function citus_extradata_container(internal) void
                 | function citus_finish_pg_upgrade() void
                 | function citus_internal.find_groupid_for_node(text,integer) integer
                 | function citus_internal.pg_dist_node_trigger_func() trigger
                 | function citus_internal.pg_dist_rebalance_strategy_enterprise_check() trigger
                 | function citus_internal.pg_dist_rebalance_strategy_trigger_func() trigger
                 | function citus_internal.pg_dist_shard_placement_trigger_func() trigger
                 | function citus_internal.refresh_isolation_tester_prepared_statement() void
                 | function citus_internal.replace_isolation_tester_func() void
                 | function citus_internal.restore_isolation_tester_func() void
                 | function citus_isolation_test_session_is_blocked(integer,integer[]) boolean
                 | function citus_json_concatenate(json,json) json
                 | function citus_json_concatenate_final(json) json
                 | function citus_jsonb_concatenate(jsonb,jsonb) jsonb
                 | function citus_jsonb_concatenate_final(jsonb) jsonb
                 | function citus_node_capacity_1(integer) real
                 | function citus_prepare_pg_upgrade() void
                 | function citus_query_stats() SETOF record
                 | function citus_relation_size(regclass) bigint
                 | function citus_server_id() uuid
                 | function citus_set_default_rebalance_strategy(text) void
                 | function citus_shard_allowed_on_node_true(bigint,integer) boolean
                 | function citus_shard_cost_1(bigint) real
                 | function citus_shard_cost_by_disk_size(bigint) real
                 | function citus_stat_statements() SETOF record
                 | function citus_stat_statements_reset() void
                 | function citus_table_is_visible(oid) boolean
                 | function citus_table_size(regclass) bigint
                 | function citus_text_send_as_jsonb(text) bytea
                 | function citus_total_relation_size(regclass) bigint
                 | function citus_truncate_trigger() trigger
                 | function citus_validate_rebalance_strategy_functions(regproc,regproc,regproc) void
                 | function citus_version() text
                 | function citus_worker_stat_activity() SETOF record
                 | function column_name_to_column(regclass,text) text
                 | function column_to_column_name(regclass,text) text
                 | function coord_combine_agg(oid,cstring,anyelement) anyelement
                 | function coord_combine_agg_ffunc(internal,oid,cstring,anyelement) anyelement
                 | function coord_combine_agg_sfunc(internal,oid,cstring,anyelement) internal
                 | function create_distributed_function(regprocedure,text,text) void
                 | function create_distributed_table(regclass,text,citus.distribution_type,text) void
                 | function create_intermediate_result(text,text) bigint
                 | function create_reference_table(regclass) void
                 | function distributed_tables_colocated(regclass,regclass) boolean
                 | function dump_global_wait_edges() SETOF record
                 | function dump_local_wait_edges() SETOF record
                 | function fetch_intermediate_results(text[],text,integer) bigint
                 | function get_all_active_transactions() SETOF record
                 | function get_colocated_shard_array(bigint) bigint[]
                 | function get_colocated_table_array(regclass) regclass[]
                 | function get_current_transaction_id() record
                 | function get_global_active_transactions() SETOF record
                 | function get_rebalance_progress() TABLE(sessionid integer, table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer, progress bigint)
                 | function get_rebalance_table_shards_plan(regclass,real,integer,bigint[],boolean,name) TABLE(table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer)
                 | function get_shard_id_for_distribution_column(regclass,"any") bigint
                 | function isolate_tenant_to_new_shard(regclass,"any",text) bigint
                 | function json_cat_agg(json) json
                 | function jsonb_cat_agg(jsonb) jsonb
                 | function lock_relation_if_exists(text,text) boolean
                 | function lock_shard_metadata(integer,bigint[]) void
                 | function lock_shard_resources(integer,bigint[]) void
                 | function mark_tables_colocated(regclass,regclass[]) void
                 | function master_activate_node(text,integer) integer
                 | function master_add_inactive_node(text,integer,integer,noderole,name) integer
                 | function master_add_node(text,integer,integer,noderole,name) integer
                 | function master_add_secondary_node(text,integer,text,integer,name) integer
                 | function master_append_table_to_shard(bigint,text,text,integer) real
                 | function master_apply_delete_command(text) integer
                 | function master_conninfo_cache_invalidate() trigger
                 | function master_copy_shard_placement(bigint,text,integer,text,integer,boolean,citus.shard_transfer_mode) void
                 | function master_create_distributed_table(regclass,text,citus.distribution_type) void
                 | function master_create_empty_shard(text) bigint
                 | function master_create_worker_shards(text,integer,integer) void
                 | function master_disable_node(text,integer) void
                 | function master_dist_local_group_cache_invalidate() trigger
                 | function master_dist_node_cache_invalidate() trigger
                 | function master_dist_object_cache_invalidate() trigger
                 | function master_dist_partition_cache_invalidate() trigger
                 | function master_dist_placement_cache_invalidate() trigger
                 | function master_dist_shard_cache_invalidate() trigger
                 | function master_drain_node(text,integer,citus.shard_transfer_mode,name) void
                 | function master_drop_all_shards(regclass,text,text) integer
                 | function master_drop_sequences(text[]) void
                 | function master_get_active_worker_nodes() SETOF record
                 | function master_get_new_placementid() bigint
                 | function master_get_new_shardid() bigint
                 | function master_get_table_ddl_events(text) SETOF text
                 | function master_get_table_metadata(text) record
                 | function master_modify_multiple_shards(text) integer
                 | function master_move_shard_placement(bigint,text,integer,text,integer,citus.shard_transfer_mode) void
                 | function master_remove_distributed_table_metadata_from_workers(regclass,text,text) void
                 | function master_remove_node(text,integer) void
                 | function master_remove_partition_metadata(regclass,text,text) void
                 | function master_run_on_worker(text[],integer[],text[],boolean) SETOF record
                 | function master_set_node_property(text,integer,text,boolean) void
                 | function master_unmark_object_distributed(oid,oid,integer) void
                 | function master_update_node(integer,text,integer,boolean,integer) void
                 | function master_update_shard_statistics(bigint) bigint
                 | function master_update_table_statistics(regclass) void
                 | function poolinfo_valid(text) boolean
                 | function read_intermediate_result(text,citus_copy_format) SETOF record
                 | function read_intermediate_results(text[],citus_copy_format) SETOF record
                 | function rebalance_table_shards(regclass,real,integer,bigint[],citus.shard_transfer_mode,boolean,name) void
                 | function recover_prepared_transactions() integer
                 | function relation_is_a_known_shard(regclass) boolean
                 | function replicate_table_shards(regclass,integer,integer,bigint[],citus.shard_transfer_mode) void
                 | function role_exists(name) boolean
                 | function run_command_on_colocated_placements(regclass,regclass,text,boolean) SETOF record
                 | function run_command_on_placements(regclass,text,boolean) SETOF record
                 | function run_command_on_shards(regclass,text,boolean) SETOF record
                 | function run_command_on_workers(text,boolean) SETOF record
                 | function shard_name(regclass,bigint) text
                 | function start_metadata_sync_to_node(text,integer) void
                 | function stop_metadata_sync_to_node(text,integer) void
                 | function task_tracker_assign_task(bigint,integer,text) void
                 | function task_tracker_cleanup_job(bigint) void
                 | function task_tracker_conninfo_cache_invalidate() trigger
                 | function task_tracker_task_status(bigint,integer) integer
                 | function upgrade_to_reference_table(regclass) void
                 | function worker_append_table_to_shard(text,text,text,integer) void
                 | function worker_apply_inter_shard_ddl_command(bigint,text,bigint,text,text) void
                 | function worker_apply_sequence_command(text) void
                 | function worker_apply_sequence_command(text,regtype) void
                 | function worker_apply_shard_ddl_command(bigint,text) void
                 | function worker_apply_shard_ddl_command(bigint,text,text) void
                 | function worker_cleanup_job_schema_cache() void
                 | function worker_create_or_replace_object(text) boolean
                 | function worker_create_schema(bigint,text) void
                 | function worker_create_truncate_trigger(regclass) void
                 | function worker_drop_distributed_table(text) void
                 | function worker_execute_sql_task(bigint,integer,text,boolean) bigint
                 | function worker_fetch_foreign_file(text,text,bigint,text[],integer[]) void
                 | function worker_fetch_partition_file(bigint,integer,integer,integer,text,integer) void
                 | function worker_hash("any") integer
                 | function worker_hash_partition_table(bigint,integer,text,text,oid,anyarray) void
                 | function worker_merge_files_and_run_query(bigint,integer,text,text) void
                 | function worker_merge_files_into_table(bigint,integer,text[],text[]) void
                 | function worker_partial_agg(oid,anyelement) cstring
                 | function worker_partial_agg_ffunc(internal) cstring
                 | function worker_partial_agg_sfunc(internal,oid,anyelement) internal
                 | function worker_partition_query_result(text,text,integer,citus.distribution_type,text[],text[],boolean) SETOF record
                 | function worker_range_partition_table(bigint,integer,text,text,oid,anyarray) void
                 | function worker_repartition_cleanup(bigint) void
                 | schema citus
                 | schema citus_internal
                 | sequence pg_dist_colocationid_seq
                 | sequence pg_dist_groupid_seq
                 | sequence pg_dist_node_nodeid_seq
                 | sequence pg_dist_placement_placementid_seq
                 | sequence pg_dist_shardid_seq
                 | table citus.pg_dist_object
                 | table pg_dist_authinfo
                 | table pg_dist_colocation
                 | table pg_dist_local_group
                 | table pg_dist_node
                 | table pg_dist_node_metadata
                 | table pg_dist_partition
                 | table pg_dist_placement
                 | table pg_dist_poolinfo
                 | table pg_dist_rebalance_strategy
                 | table pg_dist_shard
                 | table pg_dist_transaction
                 | type citus.distribution_type
                 | type citus.shard_transfer_mode
                 | type citus_copy_format
                 | type noderole
                 | view citus_dist_stat_activity
                 | view citus_lock_waits
                 | view citus_shard_indexes_on_worker
                 | view citus_shards_on_worker
                 | view citus_stat_statements
                 | view citus_worker_stat_activity
                 | view pg_dist_shard_placement
(186 rows)

-- Test downgrade to 9.2-2 from 9.2-4
ALTER EXTENSION citus UPDATE TO '9.2-4';
ALTER EXTENSION citus UPDATE TO '9.2-2';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

/*
 * As we mistakenly bumped schema version to 9.3-1 in a bad release, we support
 * updating citus schema from 9.3-1 to 9.2-4, but we do not support updates to 9.3-1.
 *
 * Hence the query below should fail.
 */
ALTER EXTENSION citus UPDATE TO '9.3-1';
ERROR:  extension "citus" has no update path from version "9.2-2" to version "9.3-1"
ALTER EXTENSION citus UPDATE TO '9.2-4';
-- Snapshot of state at 9.2-4
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test downgrade to 9.2-4 from 9.3-2
ALTER EXTENSION citus UPDATE TO '9.3-2';
ALTER EXTENSION citus UPDATE TO '9.2-4';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 9.3-2
ALTER EXTENSION citus UPDATE TO '9.3-2';
SELECT * FROM multi_extension.print_extension_changes();
                  previous_object                  |                            current_object
---------------------------------------------------------------------
 function citus_extradata_container(internal) void |
                                                   | function citus_extradata_container(internal) SETOF record
                                                   | function citus_remote_connection_stats() SETOF record
                                                   | function replicate_reference_tables() void
                                                   | function truncate_local_data_after_distributing_table(regclass) void
                                                   | function update_distributed_table_colocation(regclass,text) void
                                                   | function worker_create_or_alter_role(text,text,text) boolean
(7 rows)

-- Test downgrade to 9.3-2 from 9.4-1
ALTER EXTENSION citus UPDATE TO '9.4-1';
ALTER EXTENSION citus UPDATE TO '9.3-2';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 9.4-1
ALTER EXTENSION citus UPDATE TO '9.4-1';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object |                                                    current_object
---------------------------------------------------------------------
                 | function worker_last_saved_explain_analyze() TABLE(explain_analyze_output text, execution_duration double precision)
                 | function worker_save_query_explain_analyze(text,jsonb) SETOF record
(2 rows)

-- Test upgrade paths for backported citus_pg_upgrade functions
ALTER EXTENSION citus UPDATE TO '9.4-2';
ALTER EXTENSION citus UPDATE TO '9.4-1';
-- Should be empty result, even though the downgrade doesn't undo the upgrade, the
-- function signature doesn't change, which is reflected here.
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

ALTER EXTENSION citus UPDATE TO '9.4-2';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 9.4-1
ALTER EXTENSION citus UPDATE TO '9.4-1';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test upgrade paths for backported improvement of master_update_table_statistics function
ALTER EXTENSION citus UPDATE TO '9.4-3';
-- should see the new source code with internal function citus_update_table_statistics
SELECT prosrc FROM pg_proc WHERE proname = 'master_update_table_statistics' ORDER BY 1;
            prosrc
---------------------------------------------------------------------
 citus_update_table_statistics
(1 row)

ALTER EXTENSION citus UPDATE TO '9.4-2';
-- should see the old source code
SELECT prosrc FROM pg_proc WHERE proname = 'master_update_table_statistics' ORDER BY 1;
                               prosrc
---------------------------------------------------------------------
                                                                   +
 DECLARE                                                           +
  colocated_tables regclass[];                                     +
 BEGIN                                                             +
  SELECT get_colocated_table_array(relation) INTO colocated_tables;+
  PERFORM                                                          +
   master_update_shard_statistics(shardid)                         +
  FROM                                                             +
   pg_dist_shard                                                   +
  WHERE                                                            +
   logicalrelid = ANY (colocated_tables);                          +
 END;                                                              +

(1 row)

-- Should be empty result
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

ALTER EXTENSION citus UPDATE TO '9.4-3';
-- should see the new source code with internal function citus_update_table_statistics
SELECT prosrc FROM pg_proc WHERE proname = 'master_update_table_statistics' ORDER BY 1;
            prosrc
---------------------------------------------------------------------
 citus_update_table_statistics
(1 row)

-- Should be empty result
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 9.4-1
ALTER EXTENSION citus UPDATE TO '9.4-1';
-- should see the old source code
SELECT prosrc FROM pg_proc WHERE proname = 'master_update_table_statistics' ORDER BY 1;
                               prosrc
---------------------------------------------------------------------
                                                                   +
 DECLARE                                                           +
  colocated_tables regclass[];                                     +
 BEGIN                                                             +
  SELECT get_colocated_table_array(relation) INTO colocated_tables;+
  PERFORM                                                          +
   master_update_shard_statistics(shardid)                         +
  FROM                                                             +
   pg_dist_shard                                                   +
  WHERE                                                            +
   logicalrelid = ANY (colocated_tables);                          +
 END;                                                              +

(1 row)

-- Should be empty result
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test downgrade to 9.4-1 from 9.5-1
ALTER EXTENSION citus UPDATE TO '9.5-1';
-- TODO: This test should be moved to a valid downgrade testing suite where the downgrade is done, both on the schema and the binaries. Later changes in Citus made a C vs Schema discrepancy error here
-- BEGIN;
--   SET citus.enable_metadata_sync TO on;
--   SELECT master_add_node('localhost', :master_port, groupId=>0);
--   CREATE TABLE citus_local_table (a int);
--   SELECT create_citus_local_table('citus_local_table');
--   RESET citus.enable_metadata_sync;
--
--   -- downgrade from 9.5-1 to 9.4-1 should fail as we have a citus local table
--   ALTER EXTENSION citus UPDATE TO '9.4-1';
-- ROLLBACK;
-- now we can downgrade as there is no citus local table
ALTER EXTENSION citus UPDATE TO '9.4-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 9.5-1
ALTER EXTENSION citus UPDATE TO '9.5-1';
SELECT * FROM multi_extension.print_extension_changes();
                             previous_object                              |                             current_object
---------------------------------------------------------------------
 function master_drop_sequences(text[]) void                              |
 function task_tracker_assign_task(bigint,integer,text) void              |
 function task_tracker_cleanup_job(bigint) void                           |
 function task_tracker_conninfo_cache_invalidate() trigger                |
 function task_tracker_task_status(bigint,integer) integer                |
 function worker_execute_sql_task(bigint,integer,text,boolean) bigint     |
 function worker_merge_files_and_run_query(bigint,integer,text,text) void |
                                                                          | function create_citus_local_table(regclass) void
                                                                          | function undistribute_table(regclass) void
                                                                          | function worker_record_sequence_dependency(regclass,regclass,name) void
(10 rows)

-- Test upgrade paths for backported citus_pg_upgrade functions
ALTER EXTENSION citus UPDATE TO '9.5-2';
ALTER EXTENSION citus UPDATE TO '9.5-1';
-- Should be empty result, even though the downgrade doesn't undo the upgrade, the
-- function signature doesn't change, which is reflected here.
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

ALTER EXTENSION citus UPDATE TO '9.5-2';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 9.5-1
ALTER EXTENSION citus UPDATE TO '9.5-1';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test upgrade paths for backported improvement of master_update_table_statistics function
ALTER EXTENSION citus UPDATE TO '9.5-3';
-- should see the new source code with internal function citus_update_table_statistics
SELECT prosrc FROM pg_proc WHERE proname = 'master_update_table_statistics' ORDER BY 1;
            prosrc
---------------------------------------------------------------------
 citus_update_table_statistics
(1 row)

ALTER EXTENSION citus UPDATE TO '9.5-2';
-- should see the old source code
SELECT prosrc FROM pg_proc WHERE proname = 'master_update_table_statistics' ORDER BY 1;
                               prosrc
---------------------------------------------------------------------
                                                                   +
 DECLARE                                                           +
  colocated_tables regclass[];                                     +
 BEGIN                                                             +
  SELECT get_colocated_table_array(relation) INTO colocated_tables;+
  PERFORM                                                          +
   master_update_shard_statistics(shardid)                         +
  FROM                                                             +
   pg_dist_shard                                                   +
  WHERE                                                            +
   logicalrelid = ANY (colocated_tables);                          +
 END;                                                              +

(1 row)

-- Should be empty result
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

ALTER EXTENSION citus UPDATE TO '9.5-3';
-- should see the new source code with internal function citus_update_table_statistics
SELECT prosrc FROM pg_proc WHERE proname = 'master_update_table_statistics' ORDER BY 1;
            prosrc
---------------------------------------------------------------------
 citus_update_table_statistics
(1 row)

-- Should be empty result
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 9.5-1
ALTER EXTENSION citus UPDATE TO '9.5-1';
-- should see the old source code
SELECT prosrc FROM pg_proc WHERE proname = 'master_update_table_statistics' ORDER BY 1;
                               prosrc
---------------------------------------------------------------------
                                                                   +
 DECLARE                                                           +
  colocated_tables regclass[];                                     +
 BEGIN                                                             +
  SELECT get_colocated_table_array(relation) INTO colocated_tables;+
  PERFORM                                                          +
   master_update_shard_statistics(shardid)                         +
  FROM                                                             +
   pg_dist_shard                                                   +
  WHERE                                                            +
   logicalrelid = ANY (colocated_tables);                          +
 END;                                                              +

(1 row)

-- Should be empty result
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- We removed the upgrade paths to 10.0-1, 10.0-2 and 10.0-3 due to a bug that blocked
-- upgrades to 10.0, Therefore we test upgrades to 10.0-4 instead
-- Test downgrade to 9.5-1 from 10.0-4
ALTER EXTENSION citus UPDATE TO '10.0-4';
ALTER EXTENSION citus UPDATE TO '9.5-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 10.0-4
ALTER EXTENSION citus UPDATE TO '10.0-4';
SELECT * FROM multi_extension.print_extension_changes();
                                   previous_object                                    |                                                current_object
---------------------------------------------------------------------
 function citus_total_relation_size(regclass) bigint                                  |
 function create_citus_local_table(regclass) void                                     |
 function mark_tables_colocated(regclass,regclass[]) void                             |
 function master_conninfo_cache_invalidate() trigger                                  |
 function master_create_distributed_table(regclass,text,citus.distribution_type) void |
 function master_create_worker_shards(text,integer,integer) void                      |
 function master_dist_local_group_cache_invalidate() trigger                          |
 function master_dist_node_cache_invalidate() trigger                                 |
 function master_dist_object_cache_invalidate() trigger                               |
 function master_dist_partition_cache_invalidate() trigger                            |
 function master_dist_placement_cache_invalidate() trigger                            |
 function master_dist_shard_cache_invalidate() trigger                                |
 function master_drop_all_shards(regclass,text,text) integer                          |
 function master_modify_multiple_shards(text) integer                                 |
 function undistribute_table(regclass) void                                           |
 function upgrade_to_reference_table(regclass) void                                   |
                                                                                      | access method columnar
                                                                                      | function alter_columnar_table_reset(regclass,boolean,boolean,boolean,boolean) void
                                                                                      | function alter_columnar_table_set(regclass,integer,integer,name,integer) void
                                                                                      | function alter_distributed_table(regclass,text,integer,text,boolean) void
                                                                                      | function alter_old_partitions_set_access_method(regclass,timestamp with time zone,name)
                                                                                      | function alter_table_set_access_method(regclass,text) void
                                                                                      | function citus_activate_node(text,integer) integer
                                                                                      | function citus_add_inactive_node(text,integer,integer,noderole,name) integer
                                                                                      | function citus_add_local_table_to_metadata(regclass,boolean) void
                                                                                      | function citus_add_node(text,integer,integer,noderole,name) integer
                                                                                      | function citus_add_secondary_node(text,integer,text,integer,name) integer
                                                                                      | function citus_conninfo_cache_invalidate() trigger
                                                                                      | function citus_copy_shard_placement(bigint,text,integer,text,integer,boolean,citus.shard_transfer_mode) void
                                                                                      | function citus_disable_node(text,integer) void
                                                                                      | function citus_dist_local_group_cache_invalidate() trigger
                                                                                      | function citus_dist_node_cache_invalidate() trigger
                                                                                      | function citus_dist_object_cache_invalidate() trigger
                                                                                      | function citus_dist_partition_cache_invalidate() trigger
                                                                                      | function citus_dist_placement_cache_invalidate() trigger
                                                                                      | function citus_dist_shard_cache_invalidate() trigger
                                                                                      | function citus_drain_node(text,integer,citus.shard_transfer_mode,name) void
                                                                                      | function citus_drop_all_shards(regclass,text,text) integer
                                                                                      | function citus_get_active_worker_nodes() SETOF record
                                                                                      | function citus_internal.columnar_ensure_objects_exist() void
                                                                                      | function citus_move_shard_placement(bigint,text,integer,text,integer,citus.shard_transfer_mode) void
                                                                                      | function citus_remove_node(text,integer) void
                                                                                      | function citus_set_coordinator_host(text,integer,noderole,name) void
                                                                                      | function citus_set_node_property(text,integer,text,boolean) void
                                                                                      | function citus_shard_sizes() SETOF record
                                                                                      | function citus_total_relation_size(regclass,boolean) bigint
                                                                                      | function citus_unmark_object_distributed(oid,oid,integer) void
                                                                                      | function citus_update_node(integer,text,integer,boolean,integer) void
                                                                                      | function citus_update_shard_statistics(bigint) bigint
                                                                                      | function citus_update_table_statistics(regclass) void
                                                                                      | function columnar.columnar_handler(internal) table_am_handler
                                                                                      | function fix_pre_citus10_partitioned_table_constraint_names() SETOF regclass
                                                                                      | function fix_pre_citus10_partitioned_table_constraint_names(regclass) void
                                                                                      | function notify_constraint_dropped() void
                                                                                      | function remove_local_tables_from_metadata() void
                                                                                      | function time_partition_range(regclass) record
                                                                                      | function undistribute_table(regclass,boolean) void
                                                                                      | function worker_change_sequence_dependency(regclass,regclass,regclass) void
                                                                                      | function worker_fix_pre_citus10_partitioned_table_constraint_names(regclass,bigint,text) void
                                                                                      | schema columnar
                                                                                      | sequence columnar.storageid_seq
                                                                                      | table columnar.chunk
                                                                                      | table columnar.chunk_group
                                                                                      | table columnar.options
                                                                                      | table columnar.stripe
                                                                                      | view citus_shards
                                                                                      | view public.citus_tables
                                                                                      | view time_partitions
(68 rows)

-- check that we depend on the existence of public schema, and we can not drop it now
DROP SCHEMA public;
ERROR:  cannot drop schema public because other objects depend on it
DETAIL:  extension citus depends on schema public
HINT:  Use DROP ... CASCADE to drop the dependent objects too.
-- verify that citus_tables view is on pg_catalog if public schema is absent.
ALTER EXTENSION citus UPDATE TO '9.5-1';
DROP SCHEMA public;
ALTER EXTENSION citus UPDATE TO '10.0-4';
SELECT * FROM multi_extension.print_extension_changes();
      previous_object      |   current_object
---------------------------------------------------------------------
 view public.citus_tables  |
                           | view citus_tables
(2 rows)

-- recreate public schema, and recreate citus_tables in the public schema by default
CREATE SCHEMA public;
-- public schema is owned by pg_database_owner role
ALTER SCHEMA public OWNER TO pg_database_owner;
GRANT ALL ON SCHEMA public TO public;
ALTER EXTENSION citus UPDATE TO '9.5-1';
ALTER EXTENSION citus UPDATE TO '10.0-4';
SELECT * FROM multi_extension.print_extension_changes();
  previous_object   |      current_object
---------------------------------------------------------------------
 view citus_tables  |
                    | view public.citus_tables
(2 rows)

-- not print "HINT: " to hide current lib version
\set VERBOSITY terse
CREATE TABLE columnar_table(a INT, b INT) USING columnar;
SET citus.enable_version_checks TO ON;
SET columnar.enable_version_checks TO ON;
-- all should throw an error due to version mismatch
VACUUM FULL columnar_table;
ERROR:  loaded Citus library version differs from installed extension version
INSERT INTO columnar_table SELECT i FROM generate_series(1, 10) i;
ERROR:  loaded Citus library version differs from installed extension version
VACUUM columnar_table;
WARNING:  loaded Citus library version differs from installed extension version
TRUNCATE columnar_table;
ERROR:  loaded Citus library version differs from installed extension version
DROP TABLE columnar_table;
ERROR:  loaded Citus library version differs from installed extension version
CREATE INDEX ON columnar_table (a);
ERROR:  loaded Citus library version differs from installed extension version
ALTER TABLE columnar_table SET(columnar.compression = pglz);
ERROR:  loaded Citus library version differs from installed extension version
ALTER TABLE columnar_table RESET (columnar.compression);
ERROR:  loaded Citus library version differs from installed extension version
INSERT INTO columnar_table SELECT * FROM columnar_table;
ERROR:  loaded Citus library version differs from installed extension version
SELECT 1 FROM columnar_table; -- columnar custom scan
ERROR:  loaded Citus library version differs from installed extension version
SET columnar.enable_custom_scan TO OFF;
SELECT 1 FROM columnar_table; -- seq scan
ERROR:  loaded Citus library version differs from installed extension version
CREATE TABLE new_columnar_table (a int) USING columnar;
ERROR:  loaded Citus library version differs from installed extension version
-- disable version checks for other sessions too
ALTER SYSTEM SET citus.enable_version_checks TO OFF;
SELECT pg_reload_conf();
 pg_reload_conf
---------------------------------------------------------------------
 t
(1 row)

-- do cleanup for the rest of the tests
SET citus.enable_version_checks TO OFF;
SET columnar.enable_version_checks TO OFF;
DROP TABLE columnar_table;
RESET columnar.enable_custom_scan;
\set VERBOSITY default
-- Test downgrade to 10.0-4 from 10.1-1
ALTER EXTENSION citus UPDATE TO '10.1-1';
ALTER EXTENSION citus UPDATE TO '10.0-4';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 10.1-1
ALTER EXTENSION citus UPDATE TO '10.1-1';
SELECT * FROM multi_extension.print_extension_changes();
                                                                                                        previous_object                                                                                                        |                                                                                                                          current_object
---------------------------------------------------------------------
 function citus_add_rebalance_strategy(name,regproc,regproc,regproc,real,real) void                                                                                                                                            |
 function citus_internal.columnar_ensure_objects_exist() void                                                                                                                                                                  |
 function citus_internal.pg_dist_rebalance_strategy_enterprise_check() trigger                                                                                                                                                 |
 function create_distributed_table(regclass,text,citus.distribution_type,text) void                                                                                                                                            |
 function get_rebalance_progress() TABLE(sessionid integer, table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer, progress bigint)                 |
 function get_rebalance_table_shards_plan(regclass,real,integer,bigint[],boolean,name) TABLE(table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer) |
                                                                                                                                                                                                                               | function citus_add_rebalance_strategy(name,regproc,regproc,regproc,real,real,real) void
                                                                                                                                                                                                                               | function citus_cleanup_orphaned_shards()
                                                                                                                                                                                                                               | function citus_local_disk_space_stats() record
                                                                                                                                                                                                                               | function create_distributed_table(regclass,text,citus.distribution_type,text,integer) void
                                                                                                                                                                                                                               | function get_rebalance_progress() TABLE(sessionid integer, table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer, progress bigint, source_shard_size bigint, target_shard_size bigint)
                                                                                                                                                                                                                               | function get_rebalance_table_shards_plan(regclass,real,integer,bigint[],boolean,name,real) TABLE(table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer)
                                                                                                                                                                                                                               | function worker_partitioned_relation_size(regclass) bigint
                                                                                                                                                                                                                               | function worker_partitioned_relation_total_size(regclass) bigint
                                                                                                                                                                                                                               | function worker_partitioned_table_size(regclass) bigint
(15 rows)

-- Test downgrade to 10.1-1 from 10.2-1
ALTER EXTENSION citus UPDATE TO '10.2-1';
ALTER EXTENSION citus UPDATE TO '10.1-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 10.2-1
ALTER EXTENSION citus UPDATE TO '10.2-1';
SELECT * FROM multi_extension.print_extension_changes();
                      previous_object                       |                                                                                     current_object
---------------------------------------------------------------------
 function citus_drop_all_shards(regclass,text,text) integer |
 function stop_metadata_sync_to_node(text,integer) void     |
                                                            | function citus_drop_all_shards(regclass,text,text,boolean) integer
                                                            | function citus_internal.downgrade_columnar_storage(regclass) void
                                                            | function citus_internal.upgrade_columnar_storage(regclass) void
                                                            | function citus_internal_add_partition_metadata(regclass,"char",text,integer,"char") void
                                                            | function citus_internal_add_placement_metadata(bigint,integer,bigint,integer,bigint) void
                                                            | function citus_internal_add_shard_metadata(regclass,bigint,"char",text,text) void
                                                            | function citus_internal_delete_shard_metadata(bigint) void
                                                            | function citus_internal_update_placement_metadata(bigint,integer,integer) void
                                                            | function citus_internal_update_relation_colocation(oid,integer) void
                                                            | function create_time_partitions(regclass,interval,timestamp with time zone,timestamp with time zone) boolean
                                                            | function drop_old_time_partitions(regclass,timestamp with time zone)
                                                            | function get_missing_time_partition_ranges(regclass,interval,timestamp with time zone,timestamp with time zone) TABLE(partition_name text, range_from_value text, range_to_value text)
                                                            | function stop_metadata_sync_to_node(text,integer,boolean) void
                                                            | function worker_nextval(regclass) integer
(16 rows)

-- Test downgrade to 10.2-1 from 10.2-2
ALTER EXTENSION citus UPDATE TO '10.2-2';
ALTER EXTENSION citus UPDATE TO '10.2-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 10.2-2
ALTER EXTENSION citus UPDATE TO '10.2-2';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test downgrade to 10.2-2 from 10.2-3
ALTER EXTENSION citus UPDATE TO '10.2-3';
ALTER EXTENSION citus UPDATE TO '10.2-2';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 10.2-3
ALTER EXTENSION citus UPDATE TO '10.2-3';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test downgrade to 10.2-3 from 10.2-4
ALTER EXTENSION citus UPDATE TO '10.2-4';
ALTER EXTENSION citus UPDATE TO '10.2-3';
-- Make sure that we don't delete pg_depend entries added in
-- columnar--10.2-3--10.2-4.sql when downgrading to 10.2-3.
SELECT COUNT(*)=10
FROM pg_depend
WHERE classid = 'pg_am'::regclass::oid AND
      objid = (select oid from pg_am where amname = 'columnar') AND
      objsubid = 0 AND
      refclassid = 'pg_class'::regclass::oid AND
      refobjsubid = 0 AND
      deptype = 'n';
 ?column?
---------------------------------------------------------------------
 t
(1 row)

-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 10.2-4
ALTER EXTENSION citus UPDATE TO '10.2-4';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object |                              current_object
---------------------------------------------------------------------
                 | function citus_internal.columnar_ensure_am_depends_catalog() void
                 | function fix_all_partition_shard_index_names() SETOF regclass
                 | function fix_partition_shard_index_names(regclass) void
                 | function worker_fix_partition_shard_index_names(regclass,text,text) void
(4 rows)

-- There was a bug when downgrading to 10.2-2 from 10.2-4
-- Test that we do not have any issues with this particular downgrade
ALTER EXTENSION citus UPDATE TO '10.2-2';
ALTER EXTENSION citus UPDATE TO '10.2-4';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test downgrade to 10.2-4 from 10.2-5
ALTER EXTENSION citus UPDATE TO '10.2-5';
ALTER EXTENSION citus UPDATE TO '10.2-4';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 10.2-5
ALTER EXTENSION citus UPDATE TO '10.2-5';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Make sure that we defined dependencies from all rel objects (tables,
-- indexes, sequences ..) to columnar table access method ...
SELECT pg_class.oid INTO columnar_schema_members
FROM pg_class, pg_namespace
WHERE pg_namespace.oid=pg_class.relnamespace AND
      pg_namespace.nspname='columnar';
SELECT refobjid INTO columnar_schema_members_pg_depend
FROM pg_depend
WHERE classid = 'pg_am'::regclass::oid AND
      objid = (select oid from pg_am where amname = 'columnar') AND
      objsubid = 0 AND
      refclassid = 'pg_class'::regclass::oid AND
      refobjsubid = 0 AND
      deptype = 'n';
-- ... , so this should be empty,
(TABLE columnar_schema_members EXCEPT TABLE columnar_schema_members_pg_depend)
UNION
(TABLE columnar_schema_members_pg_depend EXCEPT TABLE columnar_schema_members);
 oid
---------------------------------------------------------------------
(0 rows)

-- ... , and both columnar_schema_members_pg_depend & columnar_schema_members
-- should have 10 entries.
SELECT COUNT(*)=10 FROM columnar_schema_members_pg_depend;
 ?column?
---------------------------------------------------------------------
 t
(1 row)

DROP TABLE columnar_schema_members, columnar_schema_members_pg_depend;
-- Use a synthetic pg_dist_shard record to show that upgrade fails
-- when there are cstore_fdw tables
INSERT INTO pg_dist_shard (logicalrelid, shardid, shardstorage) VALUES ('pg_dist_shard', 1, 'c');
ALTER EXTENSION citus UPDATE TO '11.0-1';
ERROR:  cstore_fdw tables are deprecated as of Citus 11.0
HINT:  Install Citus 10.2 and convert your cstore_fdw tables to the columnar access method before upgrading further
CONTEXT:  PL/pgSQL function inline_code_block line XX at RAISE
DELETE FROM pg_dist_shard WHERE shardid = 1;
-- partitioned table count is tracked on Citus 11 upgrade
CREATE TABLE e_transactions(order_id varchar(255) NULL, transaction_id int) PARTITION BY LIST(transaction_id);
CREATE TABLE orders_2020_07_01
PARTITION OF e_transactions FOR VALUES IN (1,2,3);
INSERT INTO pg_dist_partition VALUES ('e_transactions'::regclass,'h', '{VAR :varno 1 :varattno 1 :vartype 1043 :vartypmod 259 :varcollid 100 :varnullingrels (b) :varlevelsup 0 :varnosyn 1 :varattnosyn 1 :location -1}', 7, 's');
SELECT
	(metadata->>'partitioned_citus_table_exists_pre_11')::boolean as partitioned_citus_table_exists_pre_11,
	(metadata->>'partitioned_citus_table_exists_pre_11') IS NULL as is_null
FROM
	pg_dist_node_metadata;
 partitioned_citus_table_exists_pre_11 | is_null
---------------------------------------------------------------------
                                       | t
(1 row)

-- Test downgrade to 10.2-5 from 11.0-1
ALTER EXTENSION citus UPDATE TO '11.0-1';
SELECT
	(metadata->>'partitioned_citus_table_exists_pre_11')::boolean as partitioned_citus_table_exists_pre_11,
	(metadata->>'partitioned_citus_table_exists_pre_11') IS NULL as is_null
FROM
	pg_dist_node_metadata;
 partitioned_citus_table_exists_pre_11 | is_null
---------------------------------------------------------------------
 t                                     | f
(1 row)

DELETE FROM pg_dist_partition WHERE logicalrelid = 'e_transactions'::regclass;
DROP TABLE e_transactions;
ALTER EXTENSION citus UPDATE TO '10.2-5';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 11.0-1
ALTER EXTENSION citus UPDATE TO '11.0-1';
SELECT * FROM multi_extension.print_extension_changes();
                                                   previous_object                                                    |                                                            current_object
---------------------------------------------------------------------
 function citus_disable_node(text,integer) void                                                                       |
 function citus_dist_stat_activity() SETOF record                                                                     |
 function citus_worker_stat_activity() SETOF record                                                                   |
 function create_distributed_function(regprocedure,text,text) void                                                    |
 function master_append_table_to_shard(bigint,text,text,integer) real                                                 |
 function master_apply_delete_command(text) integer                                                                   |
 function master_get_table_metadata(text) record                                                                      |
 function worker_partition_query_result(text,text,integer,citus.distribution_type,text[],text[],boolean) SETOF record |
 table citus.pg_dist_object                                                                                           |
 view citus_worker_stat_activity                                                                                      |
                                                                                                                      | function citus_backend_gpid() bigint
                                                                                                                      | function citus_calculate_gpid(integer,integer) bigint
                                                                                                                      | function citus_check_cluster_node_health() SETOF record
                                                                                                                      | function citus_check_connection_to_node(text,integer) boolean
                                                                                                                      | function citus_coordinator_nodeid() integer
                                                                                                                      | function citus_disable_node(text,integer,boolean) void
                                                                                                                      | function citus_finalize_upgrade_to_citus11(boolean) boolean
                                                                                                                      | function citus_internal_add_colocation_metadata(integer,integer,integer,regtype,oid) void
                                                                                                                      | function citus_internal_add_object_metadata(text,text[],text[],integer,integer,boolean) void
                                                                                                                      | function citus_internal_delete_colocation_metadata(integer) void
                                                                                                                      | function citus_internal_global_blocked_processes() SETOF record
                                                                                                                      | function citus_internal_local_blocked_processes() SETOF record
                                                                                                                      | function citus_nodeid_for_gpid(bigint) integer
                                                                                                                      | function citus_nodename_for_nodeid(integer) text
                                                                                                                      | function citus_nodeport_for_nodeid(integer) integer
                                                                                                                      | function citus_pid_for_gpid(bigint) integer
                                                                                                                      | function citus_run_local_command(text) void
                                                                                                                      | function citus_shard_indexes_on_worker() SETOF record
                                                                                                                      | function citus_shards_on_worker() SETOF record
                                                                                                                      | function citus_stat_activity() SETOF record
                                                                                                                      | function create_distributed_function(regprocedure,text,text,boolean) void
                                                                                                                      | function get_nodeid_for_groupid(integer) integer
                                                                                                                      | function pg_cancel_backend(bigint) boolean
                                                                                                                      | function pg_terminate_backend(bigint,bigint) boolean
                                                                                                                      | function run_command_on_all_nodes(text,boolean,boolean) SETOF record
                                                                                                                      | function worker_create_or_replace_object(text[]) boolean
                                                                                                                      | function worker_drop_sequence_dependency(text) void
                                                                                                                      | function worker_drop_shell_table(text) void
                                                                                                                      | function worker_partition_query_result(text,text,integer,citus.distribution_type,text[],text[],boolean,boolean,boolean) SETOF record
                                                                                                                      | table pg_dist_object
                                                                                                                      | view citus_stat_activity
(41 rows)

-- Test downgrade to 11.0-1 from 11.0-2
ALTER EXTENSION citus UPDATE TO '11.0-2';
ALTER EXTENSION citus UPDATE TO '11.0-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 11.0-2
ALTER EXTENSION citus UPDATE TO '11.0-2';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object |                         current_object
---------------------------------------------------------------------
                 | function citus_finish_citus_upgrade()
                 | function citus_is_coordinator() boolean
                 | function run_command_on_coordinator(text,boolean) SETOF record
                 | function start_metadata_sync_to_all_nodes() boolean
(4 rows)

-- Test downgrade to 11.0-2 from 11.0-3
ALTER EXTENSION citus UPDATE TO '11.0-3';
ALTER EXTENSION citus UPDATE TO '11.0-2';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 11.0-3
ALTER EXTENSION citus UPDATE TO '11.0-3';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test downgrade to 11.0-3 from 11.0-4
ALTER EXTENSION citus UPDATE TO '11.0-4';
ALTER EXTENSION citus UPDATE TO '11.0-3';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 11.0-4
ALTER EXTENSION citus UPDATE TO '11.0-4';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test downgrade to 11.0-4 from 11.1-1
ALTER EXTENSION citus UPDATE TO '11.1-1';
ALTER EXTENSION citus UPDATE TO '11.0-4';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test CREATE EXTENSION when Citus already exists but Citus_Columnar does not. Should skip
CREATE EXTENSION IF NOT EXISTS citus;
NOTICE:  extension "citus" already exists, skipping
CREATE EXTENSION citus;
ERROR:  extension "citus" already exists
-- Snapshot of state at 11.1-1
ALTER EXTENSION citus UPDATE TO '11.1-1';
SELECT * FROM multi_extension.print_extension_changes();
                                                                                                                          previous_object                                                                                                                          |                                                                                                                                     current_object
---------------------------------------------------------------------
 access method columnar                                                                                                                                                                                                                                            |
 function alter_columnar_table_reset(regclass,boolean,boolean,boolean,boolean) void                                                                                                                                                                                |
 function alter_columnar_table_set(regclass,integer,integer,name,integer) void                                                                                                                                                                                     |
 function citus_copy_shard_placement(bigint,text,integer,text,integer,boolean,citus.shard_transfer_mode) void                                                                                                                                                      |
 function citus_internal.columnar_ensure_am_depends_catalog() void                                                                                                                                                                                                 |
 function citus_internal.downgrade_columnar_storage(regclass) void                                                                                                                                                                                                 |
 function citus_internal.upgrade_columnar_storage(regclass) void                                                                                                                                                                                                   |
 function columnar.columnar_handler(internal) table_am_handler                                                                                                                                                                                                     |
 function get_rebalance_progress() TABLE(sessionid integer, table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer, progress bigint, source_shard_size bigint, target_shard_size bigint) |
 function isolate_tenant_to_new_shard(regclass,"any",text) bigint                                                                                                                                                                                                  |
 function replicate_reference_tables() void                                                                                                                                                                                                                        |
 function worker_cleanup_job_schema_cache() void                                                                                                                                                                                                                   |
 function worker_create_schema(bigint,text) void                                                                                                                                                                                                                   |
 function worker_fetch_foreign_file(text,text,bigint,text[],integer[]) void                                                                                                                                                                                        |
 function worker_fetch_partition_file(bigint,integer,integer,integer,text,integer) void                                                                                                                                                                            |
 function worker_hash_partition_table(bigint,integer,text,text,oid,anyarray) void                                                                                                                                                                                  |
 function worker_merge_files_into_table(bigint,integer,text[],text[]) void                                                                                                                                                                                         |
 function worker_range_partition_table(bigint,integer,text,text,oid,anyarray) void                                                                                                                                                                                 |
 function worker_repartition_cleanup(bigint) void                                                                                                                                                                                                                  |
 schema columnar                                                                                                                                                                                                                                                   |
 sequence columnar.storageid_seq                                                                                                                                                                                                                                   |
 table columnar.chunk                                                                                                                                                                                                                                              |
 table columnar.chunk_group                                                                                                                                                                                                                                        |
 table columnar.options                                                                                                                                                                                                                                            |
 table columnar.stripe                                                                                                                                                                                                                                             |
                                                                                                                                                                                                                                                                   | function citus_cleanup_orphaned_resources()
                                                                                                                                                                                                                                                                   | function citus_copy_shard_placement(bigint,text,integer,text,integer,citus.shard_transfer_mode) void
                                                                                                                                                                                                                                                                   | function citus_internal_delete_partition_metadata(regclass) void
                                                                                                                                                                                                                                                                   | function citus_job_cancel(bigint) void
                                                                                                                                                                                                                                                                   | function citus_job_wait(bigint,citus_job_status) void
                                                                                                                                                                                                                                                                   | function citus_locks() SETOF record
                                                                                                                                                                                                                                                                   | function citus_rebalance_start(name,boolean,citus.shard_transfer_mode) bigint
                                                                                                                                                                                                                                                                   | function citus_rebalance_stop() void
                                                                                                                                                                                                                                                                   | function citus_rebalance_wait() void
                                                                                                                                                                                                                                                                   | function citus_split_shard_by_split_points(bigint,text[],integer[],citus.shard_transfer_mode) void
                                                                                                                                                                                                                                                                   | function create_distributed_table_concurrently(regclass,text,citus.distribution_type,text,integer) void
                                                                                                                                                                                                                                                                   | function get_rebalance_progress() TABLE(sessionid integer, table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer, progress bigint, source_shard_size bigint, target_shard_size bigint, operation_type text)
                                                                                                                                                                                                                                                                   | function isolate_tenant_to_new_shard(regclass,"any",text,citus.shard_transfer_mode) bigint
                                                                                                                                                                                                                                                                   | function replicate_reference_tables(citus.shard_transfer_mode) void
                                                                                                                                                                                                                                                                   | function worker_copy_table_to_node(regclass,integer) void
                                                                                                                                                                                                                                                                   | function worker_split_copy(bigint,text,split_copy_info[]) void
                                                                                                                                                                                                                                                                   | function worker_split_shard_release_dsm() void
                                                                                                                                                                                                                                                                   | function worker_split_shard_replication_setup(split_shard_info[]) SETOF replication_slot_info
                                                                                                                                                                                                                                                                   | sequence pg_dist_background_job_job_id_seq
                                                                                                                                                                                                                                                                   | sequence pg_dist_background_task_task_id_seq
                                                                                                                                                                                                                                                                   | sequence pg_dist_cleanup_recordid_seq
                                                                                                                                                                                                                                                                   | sequence pg_dist_operationid_seq
                                                                                                                                                                                                                                                                   | table pg_dist_background_job
                                                                                                                                                                                                                                                                   | table pg_dist_background_task
                                                                                                                                                                                                                                                                   | table pg_dist_background_task_depend
                                                                                                                                                                                                                                                                   | table pg_dist_cleanup
                                                                                                                                                                                                                                                                   | type citus_job_status
                                                                                                                                                                                                                                                                   | type citus_task_status
                                                                                                                                                                                                                                                                   | type replication_slot_info
                                                                                                                                                                                                                                                                   | type split_copy_info
                                                                                                                                                                                                                                                                   | type split_shard_info
                                                                                                                                                                                                                                                                   | view citus_locks
(57 rows)

-- Test downgrade to 11.1-1 from 11.2-1
ALTER EXTENSION citus UPDATE TO '11.2-1';
ALTER EXTENSION citus UPDATE TO '11.1-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 11.2-1
ALTER EXTENSION citus UPDATE TO '11.2-1';
ALTER EXTENSION citus_columnar UPDATE TO '11.2-1';
-- Make sure that we defined dependencies from all rel objects (tables,
-- indexes, sequences ..) to columnar table access method ...
SELECT pg_class.oid INTO columnar_schema_members
FROM pg_class, pg_namespace
WHERE pg_namespace.oid=pg_class.relnamespace AND
      pg_namespace.nspname='columnar_internal' AND
      pg_class.relname NOT IN ('chunk_group_pkey',
                               'chunk_pkey',
                               'options_pkey',
                               'stripe_first_row_number_idx',
                               'stripe_pkey');
SELECT refobjid INTO columnar_schema_members_pg_depend
FROM pg_depend
WHERE classid = 'pg_am'::regclass::oid AND
      objid = (select oid from pg_am where amname = 'columnar') AND
      objsubid = 0 AND
      refclassid = 'pg_class'::regclass::oid AND
      refobjsubid = 0 AND
      deptype = 'n';
-- ... , so this should be empty,
(TABLE columnar_schema_members EXCEPT TABLE columnar_schema_members_pg_depend)
UNION
(TABLE columnar_schema_members_pg_depend EXCEPT TABLE columnar_schema_members);
 oid
---------------------------------------------------------------------
(0 rows)

-- ... , and both columnar_schema_members_pg_depend & columnar_schema_members
-- should have 5 entries.
SELECT COUNT(*)=5 FROM columnar_schema_members_pg_depend;
 ?column?
---------------------------------------------------------------------
 t
(1 row)

DROP TABLE columnar_schema_members, columnar_schema_members_pg_depend;
SELECT * FROM multi_extension.print_extension_changes();
                                                                                                                                    previous_object                                                                                                                                     |                                                                                                                                                              current_object
---------------------------------------------------------------------
 function get_rebalance_progress() TABLE(sessionid integer, table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer, progress bigint, source_shard_size bigint, target_shard_size bigint, operation_type text) |
 function worker_append_table_to_shard(text,text,text,integer) void                                                                                                                                                                                                                     |
 function worker_split_shard_replication_setup(split_shard_info[]) SETOF replication_slot_info                                                                                                                                                                                          |
                                                                                                                                                                                                                                                                                        | function citus_copy_shard_placement(bigint,integer,integer,citus.shard_transfer_mode) void
                                                                                                                                                                                                                                                                                        | function citus_get_node_clock() cluster_clock
                                                                                                                                                                                                                                                                                        | function citus_get_transaction_clock() cluster_clock
                                                                                                                                                                                                                                                                                        | function citus_internal_add_placement_metadata(bigint,bigint,integer,bigint) void
                                                                                                                                                                                                                                                                                        | function citus_internal_adjust_local_clock_to_remote(cluster_clock) void
                                                                                                                                                                                                                                                                                        | function citus_is_clock_after(cluster_clock,cluster_clock) boolean
                                                                                                                                                                                                                                                                                        | function citus_job_list() TABLE(job_id bigint, state citus_job_status, job_type name, description text, started_at timestamp with time zone, finished_at timestamp with time zone)
                                                                                                                                                                                                                                                                                        | function citus_job_status(bigint,boolean) TABLE(job_id bigint, state citus_job_status, job_type name, description text, started_at timestamp with time zone, finished_at timestamp with time zone, details jsonb)
                                                                                                                                                                                                                                                                                        | function citus_move_shard_placement(bigint,integer,integer,citus.shard_transfer_mode) void
                                                                                                                                                                                                                                                                                        | function citus_rebalance_status(boolean) TABLE(job_id bigint, state citus_job_status, job_type name, description text, started_at timestamp with time zone, finished_at timestamp with time zone, details jsonb)
                                                                                                                                                                                                                                                                                        | function citus_task_wait(bigint,citus_task_status) void
                                                                                                                                                                                                                                                                                        | function cluster_clock_cmp(cluster_clock,cluster_clock) integer
                                                                                                                                                                                                                                                                                        | function cluster_clock_eq(cluster_clock,cluster_clock) boolean
                                                                                                                                                                                                                                                                                        | function cluster_clock_ge(cluster_clock,cluster_clock) boolean
                                                                                                                                                                                                                                                                                        | function cluster_clock_gt(cluster_clock,cluster_clock) boolean
                                                                                                                                                                                                                                                                                        | function cluster_clock_in(cstring) cluster_clock
                                                                                                                                                                                                                                                                                        | function cluster_clock_le(cluster_clock,cluster_clock) boolean
                                                                                                                                                                                                                                                                                        | function cluster_clock_logical(cluster_clock) bigint
                                                                                                                                                                                                                                                                                        | function cluster_clock_lt(cluster_clock,cluster_clock) boolean
                                                                                                                                                                                                                                                                                        | function cluster_clock_ne(cluster_clock,cluster_clock) boolean
                                                                                                                                                                                                                                                                                        | function cluster_clock_out(cluster_clock) cstring
                                                                                                                                                                                                                                                                                        | function cluster_clock_recv(internal) cluster_clock
                                                                                                                                                                                                                                                                                        | function cluster_clock_send(cluster_clock) bytea
                                                                                                                                                                                                                                                                                        | function get_rebalance_progress() TABLE(sessionid integer, table_name regclass, shardid bigint, shard_size bigint, sourcename text, sourceport integer, targetname text, targetport integer, progress bigint, source_shard_size bigint, target_shard_size bigint, operation_type text, source_lsn pg_lsn, target_lsn pg_lsn, status text)
                                                                                                                                                                                                                                                                                        | function worker_split_shard_replication_setup(split_shard_info[],bigint) SETOF replication_slot_info
                                                                                                                                                                                                                                                                                        | operator <(cluster_clock,cluster_clock)
                                                                                                                                                                                                                                                                                        | operator <=(cluster_clock,cluster_clock)
                                                                                                                                                                                                                                                                                        | operator <>(cluster_clock,cluster_clock)
                                                                                                                                                                                                                                                                                        | operator =(cluster_clock,cluster_clock)
                                                                                                                                                                                                                                                                                        | operator >(cluster_clock,cluster_clock)
                                                                                                                                                                                                                                                                                        | operator >=(cluster_clock,cluster_clock)
                                                                                                                                                                                                                                                                                        | operator class cluster_clock_ops for access method btree
                                                                                                                                                                                                                                                                                        | operator family cluster_clock_ops for access method btree
                                                                                                                                                                                                                                                                                        | sequence pg_dist_clock_logical_seq
                                                                                                                                                                                                                                                                                        | type cluster_clock
(38 rows)

-- Test downgrade to 11.2-1 from 11.2-2
ALTER EXTENSION citus UPDATE TO '11.2-2';
ALTER EXTENSION citus UPDATE TO '11.2-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 11.2-2
ALTER EXTENSION citus UPDATE TO '11.2-2';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object |                          current_object
---------------------------------------------------------------------
                 | function worker_adjust_identity_column_seq_ranges(regclass) void
(1 row)

-- Test downgrade to 11.2-2 from 11.3-1
ALTER EXTENSION citus UPDATE TO '11.3-1';
ALTER EXTENSION citus UPDATE TO '11.2-2';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 11.3-1
ALTER EXTENSION citus UPDATE TO '11.3-1';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object |                             current_object
---------------------------------------------------------------------
                 | function citus_internal_is_replication_origin_tracking_active() boolean
                 | function citus_internal_mark_node_not_synced(integer,integer) void
                 | function citus_internal_start_replication_origin_tracking() void
                 | function citus_internal_stop_replication_origin_tracking() void
                 | function citus_stat_tenants(boolean) SETOF record
                 | function citus_stat_tenants_local(boolean) SETOF record
                 | function citus_stat_tenants_local_reset() void
                 | function citus_stat_tenants_reset() void
                 | function worker_drop_all_shell_tables(boolean)
                 | view citus_stat_tenants
                 | view citus_stat_tenants_local
(11 rows)

-- Test downgrade to 11.3-1 from 11.3-2
ALTER EXTENSION citus UPDATE TO '11.3-2';
ALTER EXTENSION citus UPDATE TO '11.3-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 11.3-2
ALTER EXTENSION citus UPDATE TO '11.3-2';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test downgrade to 11.3-2 from 12.0-1
ALTER EXTENSION citus UPDATE TO '12.0-1';
CREATE TABLE null_shard_key (x int, y int);
SET citus.shard_replication_factor TO 1;
SELECT create_distributed_table('null_shard_key', null);
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- Show that we cannot downgrade to 11.3-2 becuase the cluster has a
-- distributed table with single-shard.
ALTER EXTENSION citus UPDATE TO '11.3-2';
ERROR:  cannot downgrade Citus because there are distributed tables without a shard key.
DETAIL:  To downgrade Citus to an older version, you should first convert those tables to Postgres tables by executing SELECT undistribute_table("%s").
HINT:  You can find the distributed tables without a shard key in the cluster by using the following query: "SELECT * FROM citus_tables WHERE distribution_column = '<none>' AND colocation_id > 0".
CONTEXT:  PL/pgSQL function inline_code_block line XX at RAISE
DROP TABLE null_shard_key;
ALTER EXTENSION citus UPDATE TO '11.3-2';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 12.0-1
ALTER EXTENSION citus UPDATE TO '12.0-1';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object |                              current_object
---------------------------------------------------------------------
                 | function citus_internal_add_tenant_schema(oid,integer) void
                 | function citus_internal_delete_tenant_schema(oid) void
                 | function citus_internal_unregister_tenant_schema_globally(oid,text) void
                 | function citus_schema_distribute(regnamespace) void
                 | function citus_schema_undistribute(regnamespace) void
                 | function citus_stat_tenants_local_internal(boolean) SETOF record
                 | table pg_dist_schema
                 | view public.citus_schemas
(8 rows)

-- Test downgrade to 12.0-1 from 12.1-1
ALTER EXTENSION citus UPDATE TO '12.1-1';
ALTER EXTENSION citus UPDATE TO '12.0-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 12.1-1
ALTER EXTENSION citus UPDATE TO '12.1-1';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object |                                     current_object
---------------------------------------------------------------------
                 | function citus_internal_delete_placement_metadata(bigint) void
                 | function citus_internal_update_none_dist_table_metadata(oid,"char",bigint,boolean) void
                 | function citus_pause_node_within_txn(integer,boolean,integer) void
                 | function citus_schema_move(regnamespace,integer,citus.shard_transfer_mode) void
                 | function citus_schema_move(regnamespace,text,integer,citus.shard_transfer_mode) void
(5 rows)

-- Test downgrade to 12.1-1 from 13.0-1
ALTER EXTENSION citus UPDATE TO '13.0-1';
ALTER EXTENSION citus UPDATE TO '12.1-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 13.0-1
ALTER EXTENSION citus UPDATE TO '13.0-1';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Test downgrade to 13.0-1 from 13.1-1
ALTER EXTENSION citus UPDATE TO '13.1-1';
ALTER EXTENSION citus UPDATE TO '13.0-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 13.1-1
ALTER EXTENSION citus UPDATE TO '13.1-1';
SELECT * FROM multi_extension.print_extension_changes();
                        previous_object                         |                                        current_object
---------------------------------------------------------------------
 function citus_unmark_object_distributed(oid,oid,integer) void |
 function shard_name(regclass,bigint) text                      |
                                                                | function citus_internal.acquire_citus_advisory_object_class_lock(integer,cstring) void
                                                                | function citus_internal.add_colocation_metadata(integer,integer,integer,regtype,oid) void
                                                                | function citus_internal.add_object_metadata(text,text[],text[],integer,integer,boolean) void
                                                                | function citus_internal.add_partition_metadata(regclass,"char",text,integer,"char") void
                                                                | function citus_internal.add_placement_metadata(bigint,bigint,integer,bigint) void
                                                                | function citus_internal.add_shard_metadata(regclass,bigint,"char",text,text) void
                                                                | function citus_internal.add_tenant_schema(oid,integer) void
                                                                | function citus_internal.adjust_local_clock_to_remote(cluster_clock) void
                                                                | function citus_internal.database_command(text) void
                                                                | function citus_internal.delete_colocation_metadata(integer) void
                                                                | function citus_internal.delete_partition_metadata(regclass) void
                                                                | function citus_internal.delete_placement_metadata(bigint) void
                                                                | function citus_internal.delete_shard_metadata(bigint) void
                                                                | function citus_internal.delete_tenant_schema(oid) void
                                                                | function citus_internal.global_blocked_processes() SETOF record
                                                                | function citus_internal.is_replication_origin_tracking_active() boolean
                                                                | function citus_internal.local_blocked_processes() SETOF record
                                                                | function citus_internal.mark_node_not_synced(integer,integer) void
                                                                | function citus_internal.start_replication_origin_tracking() void
                                                                | function citus_internal.stop_replication_origin_tracking() void
                                                                | function citus_internal.unregister_tenant_schema_globally(oid,text) void
                                                                | function citus_internal.update_none_dist_table_metadata(oid,"char",bigint,boolean) void
                                                                | function citus_internal.update_placement_metadata(bigint,integer,integer) void
                                                                | function citus_internal.update_relation_colocation(oid,integer) void
                                                                | function citus_is_primary_node() boolean
                                                                | function citus_stat_counters(oid) SETOF record
                                                                | function citus_stat_counters_reset(oid) void
                                                                | function citus_unmark_object_distributed(oid,oid,integer,boolean) void
                                                                | function shard_name(regclass,bigint,boolean) text
                                                                | view citus_nodes
                                                                | view citus_stat_counters
(33 rows)

-- Test downgrade to 13.1-1 from 13.2-1
ALTER EXTENSION citus UPDATE TO '13.2-1';
ALTER EXTENSION citus UPDATE TO '13.1-1';
-- Should be empty result since upgrade+downgrade should be a no-op
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

-- Snapshot of state at 13.2-1
ALTER EXTENSION citus UPDATE TO '13.2-1';
SELECT * FROM multi_extension.print_extension_changes();
 previous_object | current_object
---------------------------------------------------------------------
(0 rows)

DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff;
-- show running version
SHOW citus.version;
 citus.version
---------------------------------------------------------------------
 13.2devel
(1 row)

-- ensure no unexpected objects were created outside pg_catalog
SELECT pgio.type, pgio.identity
FROM pg_depend AS pgd,
	 pg_extension AS pge,
	 LATERAL pg_identify_object(pgd.classid, pgd.objid, pgd.objsubid) AS pgio
WHERE pgd.refclassid = 'pg_extension'::regclass AND
	  pgd.refobjid   = pge.oid AND
	  pge.extname    = 'citus' AND
	  pgio.schema    NOT IN ('pg_catalog', 'citus', 'citus_internal', 'test', 'columnar', 'columnar_internal') AND
      pgio.type      != 'type'
ORDER BY 1, 2;
 type |       identity
---------------------------------------------------------------------
 view | public.citus_schemas
 view | public.citus_tables
(2 rows)

-- see incompatible version errors out
RESET citus.enable_version_checks;
RESET columnar.enable_version_checks;
-- reset version check config for other sessions too
ALTER SYSTEM RESET citus.enable_version_checks;
SELECT pg_reload_conf();
 pg_reload_conf
---------------------------------------------------------------------
 t
(1 row)

DROP EXTENSION citus;
DROP EXTENSION citus_columnar;
CREATE EXTENSION citus VERSION '8.0-1';
ERROR:  specified version incompatible with loaded Citus library
DETAIL:  Loaded library requires 13.2, but 8.0-1 was specified.
HINT:  If a newer library is present, restart the database and try the command again.
-- Test non-distributed queries work even in version mismatch
SET citus.enable_version_checks TO 'false';
SET columnar.enable_version_checks TO 'false';
CREATE EXTENSION citus VERSION '8.1-1';
SET citus.enable_version_checks TO 'true';
SET columnar.enable_version_checks TO 'true';
-- Test CREATE TABLE
CREATE TABLE version_mismatch_table(column1 int);
-- Test COPY
\copy version_mismatch_table FROM STDIN;
-- Test INSERT
INSERT INTO version_mismatch_table(column1) VALUES(5);
-- Test SELECT
SELECT * FROM version_mismatch_table ORDER BY column1;
 column1
---------------------------------------------------------------------
       0
       1
       2
       3
       4
       5
(6 rows)

-- Test SELECT from pg_catalog
SELECT d.datname as "Name",
       pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
       pg_catalog.array_to_string(d.datacl, E'\n') AS "Access privileges"
FROM pg_catalog.pg_database d
ORDER BY 1;
    Name    |  Owner   |   Access privileges
---------------------------------------------------------------------
 postgres   | postgres |
 regression | postgres |
 template0  | postgres | =c/postgres          +
            |          | postgres=CTc/postgres
 template1  | postgres | =c/postgres          +
            |          | postgres=CTc/postgres
(4 rows)

-- We should not distribute table in version mistmatch
SELECT create_distributed_table('version_mismatch_table', 'column1');
ERROR:  loaded Citus library version differs from installed extension version
DETAIL:  Loaded library requires 13.2, but the installed extension version is 8.1-1.
HINT:  Run ALTER EXTENSION citus UPDATE and try again.
-- This function will cause fail in next ALTER EXTENSION
CREATE OR REPLACE FUNCTION pg_catalog.relation_is_a_known_shard(regclass)
RETURNS void LANGUAGE plpgsql
AS $function$
BEGIN
END;
$function$;
ERROR:  cannot change return type of existing function
HINT:  Use DROP FUNCTION relation_is_a_known_shard(regclass) first.
SET citus.enable_version_checks TO 'false';
SET columnar.enable_version_checks TO 'false';
-- This will fail because of previous function declaration
ALTER EXTENSION citus UPDATE TO '9.1-1';
-- We can DROP problematic function and continue ALTER EXTENSION even when version checks are on
SET citus.enable_version_checks TO 'true';
SET columnar.enable_version_checks TO 'true';
DROP FUNCTION pg_catalog.relation_is_a_known_shard(regclass);
ERROR:  cannot drop function relation_is_a_known_shard(regclass) because extension citus requires it
HINT:  You can drop extension citus instead.
SET citus.enable_version_checks TO 'false';
SET columnar.enable_version_checks TO 'false';
ALTER EXTENSION citus UPDATE TO '9.1-1';
NOTICE:  version "9.1-1" of extension "citus" is already installed
-- Test updating to the latest version without specifying the version number
ALTER EXTENSION citus UPDATE;
-- re-create in newest version
DROP EXTENSION citus;
DROP EXTENSION citus_columnar;
\c
CREATE EXTENSION citus;
-- test cache invalidation in workers
\c - - - :worker_1_port
DROP EXTENSION citus;
DROP EXTENSION citus_columnar;
SET citus.enable_version_checks TO 'false';
SET columnar.enable_version_checks TO 'false';
CREATE EXTENSION citus VERSION '8.0-1';
SET citus.enable_version_checks TO 'true';
SET columnar.enable_version_checks TO 'true';
-- during ALTER EXTENSION, we should invalidate the cache
ALTER EXTENSION citus UPDATE;
-- if cache is invalidated succesfull, this \d should work without any problem
\d
            List of relations
 Schema |     Name      | Type |  Owner
---------------------------------------------------------------------
 public | citus_schemas | view | postgres
 public | citus_tables  | view | postgres
(2 rows)

\c - - - :master_port
-- test https://github.com/citusdata/citus/issues/3409
CREATE USER testuser2 SUPERUSER;
SET ROLE testuser2;
DROP EXTENSION Citus;
-- Loop until we see there's no maintenance daemon running
DO $$begin
    for i in 0 .. 100 loop
        if i = 100 then raise 'Waited too long'; end if;
        PERFORM pg_stat_clear_snapshot();
        perform * from pg_stat_activity where application_name = 'Citus Maintenance Daemon';
        if not found then exit; end if;
        perform pg_sleep(0.1);
    end loop;
end$$;
SELECT datid, datname, usename FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon';
 datid | datname | usename
---------------------------------------------------------------------
(0 rows)

CREATE EXTENSION Citus;
-- Loop until we there's a maintenance daemon running
DO $$begin
    for i in 0 .. 100 loop
        if i = 100 then raise 'Waited too long'; end if;
        PERFORM pg_stat_clear_snapshot();
        perform * from pg_stat_activity where application_name = 'Citus Maintenance Daemon';
        if found then exit; end if;
        perform pg_sleep(0.1);
    end loop;
end$$;
SELECT datid, datname, usename FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon';
 datid |  datname   |  usename
---------------------------------------------------------------------
 16384 | regression | testuser2
(1 row)

RESET ROLE;
-- check that maintenance daemon gets (re-)started for the right user
DROP EXTENSION citus;
CREATE USER testuser SUPERUSER;
SET ROLE testuser;
CREATE EXTENSION citus;
SELECT datname, current_database(),
    usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus')
FROM test.maintenance_worker();
  datname   | current_database | usename  | extowner
---------------------------------------------------------------------
 regression | regression       | testuser | testuser
(1 row)

-- and recreate as the right owner
RESET ROLE;
DROP EXTENSION citus;
CREATE EXTENSION citus;
-- Check that maintenance daemon can also be started in another database
CREATE DATABASE another;
NOTICE:  Citus partially supports CREATE DATABASE for distributed databases
DETAIL:  Citus does not propagate CREATE DATABASE command to other nodes
HINT:  You can manually create a database and its extensions on other nodes.
\c another
CREATE EXTENSION citus;
CREATE SCHEMA test;
:create_function_test_maintenance_worker
-- see that the daemon started
SELECT datname, current_database(),
    usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus')
FROM test.maintenance_worker();
 datname | current_database | usename  | extowner
---------------------------------------------------------------------
 another | another          | postgres | postgres
(1 row)

-- Test that database with active worker can be dropped.
\c regression
CREATE SCHEMA test_daemon;
-- we create a similar function on the regression database
-- note that this function checks for the existence of the daemon
-- when not found, returns true else tries for 5 times and
-- returns false
CREATE OR REPLACE FUNCTION test_daemon.maintenance_daemon_died(p_dbname text)
    RETURNS boolean
    LANGUAGE plpgsql
AS $$
DECLARE
   activity record;
BEGIN
    PERFORM pg_stat_clear_snapshot();
    SELECT * INTO activity FROM pg_stat_activity
    WHERE application_name = 'Citus Maintenance Daemon' AND datname = p_dbname;
    IF activity.pid IS NULL THEN
        RETURN true;
    ELSE
        RETURN false;
    END IF;
END;
$$;
-- drop the database and see that the daemon is dead
DROP DATABASE another;
SELECT
    *
FROM
    test_daemon.maintenance_daemon_died('another');
 maintenance_daemon_died
---------------------------------------------------------------------
 t
(1 row)

-- we don't need the schema and the function anymore
DROP SCHEMA test_daemon CASCADE;
NOTICE:  drop cascades to function test_daemon.maintenance_daemon_died(text)
-- verify citus does not crash while creating a table when run against an older worker
-- create_distributed_table piggybacks multiple commands into single one, if one worker
-- did not have the required UDF it should fail instead of crash.
-- create a test database, configure citus with single node
CREATE DATABASE another;
NOTICE:  Citus partially supports CREATE DATABASE for distributed databases
DETAIL:  Citus does not propagate CREATE DATABASE command to other nodes
HINT:  You can manually create a database and its extensions on other nodes.
\c - - - :worker_1_port
CREATE DATABASE another;
NOTICE:  Citus partially supports CREATE DATABASE for distributed databases
DETAIL:  Citus does not propagate CREATE DATABASE command to other nodes
HINT:  You can manually create a database and its extensions on other nodes.
\c - - - :master_port
\c another
CREATE EXTENSION citus;
\c - - - :worker_1_port
CREATE EXTENSION citus;
\c - - - :master_port
SELECT FROM master_add_node('localhost', :worker_1_port);
--
(1 row)

\c - - - :worker_1_port
ALTER FUNCTION assign_distributed_transaction_id(initiator_node_identifier integer, transaction_number bigint, transaction_stamp timestamp with time zone)
RENAME TO dummy_assign_function;
\c - - - :master_port
SET citus.shard_replication_factor to 1;
-- create_distributed_table command should fail
CREATE TABLE t1(a int, b int);
SET client_min_messages TO ERROR;
DO $$
BEGIN
        BEGIN
                SELECT create_distributed_table('t1', 'a');
        EXCEPTION WHEN OTHERS THEN
                RAISE 'create distributed table failed';
        END;
END;
$$;
ERROR:  create distributed table failed
CONTEXT:  PL/pgSQL function inline_code_block line XX at RAISE
\c regression
\c - - - :master_port
DROP DATABASE another;
\c - - - :worker_1_port
DROP DATABASE another;
\c - - - :master_port
-- only the regression database should have a maintenance daemon
SELECT count(*) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon';
 count
---------------------------------------------------------------------
     1
(1 row)

-- recreate the extension immediately after the maintenancae daemon errors
SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon';
 pg_cancel_backend
---------------------------------------------------------------------
 t
(1 row)

DROP EXTENSION citus;
CREATE EXTENSION citus;
-- wait for maintenance daemon restart
SELECT datname, current_database(),
    usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus')
FROM test.maintenance_worker();
  datname   | current_database | usename  | extowner
---------------------------------------------------------------------
 regression | regression       | postgres | postgres
(1 row)

-- confirm that there is only one maintenance daemon
SELECT count(*) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon';
 count
---------------------------------------------------------------------
     1
(1 row)

-- kill the maintenance daemon
SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon';
 pg_cancel_backend
---------------------------------------------------------------------
 t
(1 row)

-- reconnect
\c - - - :master_port
-- run something that goes through planner hook and therefore kicks of maintenance daemon
SELECT 1;
 ?column?
---------------------------------------------------------------------
        1
(1 row)

-- wait for maintenance daemon restart
SELECT datname, current_database(),
    usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus')
FROM test.maintenance_worker();
  datname   | current_database | usename  | extowner
---------------------------------------------------------------------
 regression | regression       | postgres | postgres
(1 row)

-- confirm that there is only one maintenance daemon
SELECT count(*) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon';
 count
---------------------------------------------------------------------
     1
(1 row)

-- confirm that we can create a distributed table concurrently on an empty node
DROP EXTENSION citus;
CREATE EXTENSION citus;
CREATE TABLE test (x int, y int);
INSERT INTO test VALUES (1,2);
SET citus.shard_replication_factor TO 1;
SELECT create_distributed_table_concurrently('test','x');
NOTICE:  relation test does not have a REPLICA IDENTITY or PRIMARY KEY
DETAIL:  UPDATE and DELETE commands on the relation will error out during create_distributed_table_concurrently unless there is a REPLICA IDENTITY or PRIMARY KEY. INSERT commands will still work.
 create_distributed_table_concurrently
---------------------------------------------------------------------

(1 row)

DROP TABLE test;
TRUNCATE pg_dist_node;
-- confirm that we can create a distributed table on an empty node
CREATE TABLE test (x int, y int);
INSERT INTO test VALUES (1,2);
SET citus.shard_replication_factor TO 1;
SELECT create_distributed_table('test','x');
NOTICE:  Copying data from local table...
NOTICE:  copying the data has completed
DETAIL:  The local data in the table is no longer visible, but is still on disk.
HINT:  To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$public.test$$)
 create_distributed_table
---------------------------------------------------------------------

(1 row)

DROP TABLE test;
TRUNCATE pg_dist_node;
-- confirm that we can create a single-shard table on an empty node
CREATE TABLE test (x int, y int);
INSERT INTO test VALUES (1,2);
SET citus.shard_replication_factor TO 1;
SELECT create_distributed_table('test', null, colocate_with=>'none', distribution_type=>null);
NOTICE:  Copying data from local table...
NOTICE:  copying the data has completed
DETAIL:  The local data in the table is no longer visible, but is still on disk.
HINT:  To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$public.test$$)
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- and make sure that we can't remove the coordinator due to "test"
SELECT citus_remove_node('localhost', :master_port);
ERROR:  cannot remove or disable the node localhost:xxxxx because because it contains the only shard placement for shard xxxxx
DETAIL:  One of the table(s) that prevents the operation complete successfully is public.test
HINT:  To proceed, either drop the tables or use undistribute_table() function to convert them to local tables
DROP TABLE test;
-- and now we should be able to remove the coordinator
SELECT citus_remove_node('localhost', :master_port);
 citus_remove_node
---------------------------------------------------------------------

(1 row)

-- confirm that we can create a tenant schema / table on an empty node
SET citus.enable_schema_based_sharding TO ON;
CREATE SCHEMA tenant_schema;
CREATE TABLE tenant_schema.test(x int, y int);
SELECT colocationid = (
    SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_schema.test'::regclass
)
FROM pg_dist_schema
WHERE schemaid::regnamespace::text = 'tenant_schema';
 ?column?
---------------------------------------------------------------------
 t
(1 row)

-- and make sure that we can't remove the coordinator due to "test"
SELECT citus_remove_node('localhost', :master_port);
ERROR:  cannot remove or disable the node localhost:xxxxx because because it contains the only shard placement for shard xxxxx
DETAIL:  One of the table(s) that prevents the operation complete successfully is tenant_schema.test
HINT:  To proceed, either drop the tables or use undistribute_table() function to convert them to local tables
BEGIN;
  SET LOCAL client_min_messages TO WARNING;
  DROP SCHEMA tenant_schema CASCADE;
COMMIT;
-- and now we should be able to remove the coordinator
SELECT citus_remove_node('localhost', :master_port);
 citus_remove_node
---------------------------------------------------------------------

(1 row)

CREATE SCHEMA tenant_schema;
-- Make sure that we can sync metadata for empty tenant schemas
-- when adding the first node to the cluster.
SELECT 1 FROM citus_add_node('localhost', :worker_1_port);
 ?column?
---------------------------------------------------------------------
        1
(1 row)

DROP SCHEMA tenant_schema;
SELECT citus_remove_node('localhost', :worker_1_port);
 citus_remove_node
---------------------------------------------------------------------

(1 row)

RESET citus.enable_schema_based_sharding;
-- confirm that we can create a reference table on an empty node
CREATE TABLE test (x int, y int);
INSERT INTO test VALUES (1,2);
SELECT create_reference_table('test');
NOTICE:  Copying data from local table...
NOTICE:  copying the data has completed
DETAIL:  The local data in the table is no longer visible, but is still on disk.
HINT:  To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$public.test$$)
 create_reference_table
---------------------------------------------------------------------

(1 row)

DROP TABLE test;
TRUNCATE pg_dist_node;
-- confirm that we can create a local table on an empty node
CREATE TABLE test (x int, y int);
INSERT INTO test VALUES (1,2);
SELECT citus_add_local_table_to_metadata('test');
 citus_add_local_table_to_metadata
---------------------------------------------------------------------

(1 row)

DROP TABLE test;
-- Verify that we don't consider the schemas created by extensions as tenant schemas.
-- Easiest way of verifying this is to drop and re-create columnar extension.
DROP EXTENSION citus_columnar;
SET citus.enable_schema_based_sharding TO ON;
CREATE EXTENSION citus_columnar;
SELECT COUNT(*)=0 FROM pg_dist_schema
WHERE schemaid IN ('columnar'::regnamespace, 'columnar_internal'::regnamespace);
 ?column?
---------------------------------------------------------------------
 t
(1 row)

RESET citus.enable_schema_based_sharding;
DROP EXTENSION citus;
CREATE EXTENSION citus;
DROP TABLE version_mismatch_table;
DROP TABLE  multi_extension.extension_basic_types;
DROP SCHEMA multi_extension;
ERROR:  cannot drop schema multi_extension because other objects depend on it
DETAIL:  function multi_extension.print_extension_changes() depends on schema multi_extension
HINT:  Use DROP ... CASCADE to drop the dependent objects too.
