CREATE SCHEMA text_search;
CREATE SCHEMA text_search2;
SET search_path TO text_search;
-- create a new configuration from scratch
CREATE TEXT SEARCH CONFIGURATION my_text_search_config ( parser = default );
CREATE TABLE t1(id int, name text);
CREATE INDEX t1_search_name ON t1 USING gin (to_tsvector('text_search.my_text_search_config'::regconfig, (COALESCE(name, ''::character varying))::text));
SELECT create_distributed_table('t1', 'name');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

DROP TABLE t1;
DROP TEXT SEARCH CONFIGURATION my_text_search_config;
-- try to create table and index in 1 transaction
BEGIN;
CREATE TEXT SEARCH CONFIGURATION my_text_search_config ( parser = default );
CREATE TABLE t1(id int, name text);
CREATE INDEX t1_search_name ON t1 USING gin (to_tsvector('text_search.my_text_search_config'::regconfig, (COALESCE(name, ''::character varying))::text));
SELECT create_distributed_table('t1', 'name');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

ABORT;
-- try again, should not fail with my_text_search_config being retained on the worker
BEGIN;
CREATE TEXT SEARCH CONFIGURATION my_text_search_config ( parser = default );
COMMENT ON TEXT SEARCH CONFIGURATION my_text_search_config IS 'on demand propagation of text search object with a comment';
CREATE TABLE t1(id int, name text);
CREATE INDEX t1_search_name ON t1 USING gin (to_tsvector('text_search.my_text_search_config'::regconfig, (COALESCE(name, ''::character varying))::text));
SELECT create_distributed_table('t1', 'name');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- verify that we can change the object
COMMENT ON TEXT SEARCH CONFIGURATION my_text_search_config  IS 'this comment can be set right now';
COMMIT;
SELECT * FROM run_command_on_workers($$
    SELECT obj_description('text_search.my_text_search_config'::regconfig);
$$) ORDER BY 1,2;
 nodename  | nodeport | success |              result
---------------------------------------------------------------------
 localhost |    57637 | t       | this comment can be set right now
 localhost |    57638 | t       | this comment can be set right now
(2 rows)

DROP TABLE t1;
-- create an index on an already distributed table
BEGIN;
CREATE TEXT SEARCH CONFIGURATION my_text_search_config2 ( parser = default );
COMMENT ON TEXT SEARCH CONFIGURATION my_text_search_config2 IS 'on demand propagation of text search object with a comment 2';
CREATE TABLE t1(id int, name text);
SELECT create_distributed_table('t1', 'name');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE INDEX t1_search_name ON t1 USING gin (to_tsvector('text_search.my_text_search_config2'::regconfig, (COALESCE(name, ''::character varying))::text));
COMMIT;
SELECT * FROM run_command_on_workers($$
    SELECT obj_description('text_search.my_text_search_config2'::regconfig);
$$) ORDER BY 1,2;
 nodename  | nodeport | success |                            result
---------------------------------------------------------------------
 localhost |    57637 | t       | on demand propagation of text search object with a comment 2
 localhost |    57638 | t       | on demand propagation of text search object with a comment 2
(2 rows)

DROP TABLE t1;
-- should be able to create a configuration based on a copy of an existing configuration
CREATE TEXT SEARCH CONFIGURATION french_noaccent ( COPY = french );
CREATE TABLE t2(id int, name text);
CREATE INDEX t2_search_name ON t2 USING gin (to_tsvector('text_search.french_noaccent'::regconfig, (COALESCE(name, ''::character varying))::text));
SELECT create_distributed_table('t2', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- spot check that french_noaccent copied settings from french
SELECT * FROM run_command_on_workers($$
    SELECT ROW(alias,dictionary) FROM ts_debug('text_search.french_noaccent', 'comment tu t''appelle') WHERE alias = 'asciiword' LIMIT 1;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |         result
---------------------------------------------------------------------
 localhost |    57637 | t       | (asciiword,french_stem)
 localhost |    57638 | t       | (asciiword,french_stem)
(2 rows)

-- makes no sense, however we expect that the dictionary for the first token changes accordingly
ALTER TEXT SEARCH CONFIGURATION french_noaccent ALTER MAPPING FOR asciiword WITH dutch_stem;
SELECT * FROM run_command_on_workers($$
    SELECT ROW(alias,dictionary) FROM ts_debug('text_search.french_noaccent', 'comment tu t''appelle') WHERE alias = 'asciiword' LIMIT 1;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |         result
---------------------------------------------------------------------
 localhost |    57637 | t       | (asciiword,dutch_stem)
 localhost |    57638 | t       | (asciiword,dutch_stem)
(2 rows)

-- do the same but we will replace all french dictionaries
SELECT * FROM run_command_on_workers($$
    SELECT ROW(alias,dictionary) FROM ts_debug('text_search.french_noaccent', 'un chou-fleur') WHERE alias = 'asciihword' LIMIT 1;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |          result
---------------------------------------------------------------------
 localhost |    57637 | t       | (asciihword,french_stem)
 localhost |    57638 | t       | (asciihword,french_stem)
(2 rows)

ALTER TEXT SEARCH CONFIGURATION french_noaccent ALTER MAPPING REPLACE french_stem WITH dutch_stem;
SELECT * FROM run_command_on_workers($$
    SELECT ROW(alias,dictionary) FROM ts_debug('text_search.french_noaccent', 'un chou-fleur') WHERE alias = 'asciihword' LIMIT 1;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |         result
---------------------------------------------------------------------
 localhost |    57637 | t       | (asciihword,dutch_stem)
 localhost |    57638 | t       | (asciihword,dutch_stem)
(2 rows)

-- once more but now back via yet a different DDL command
ALTER TEXT SEARCH CONFIGURATION french_noaccent ALTER MAPPING FOR asciihword REPLACE dutch_stem WITH french_stem;
SELECT * FROM run_command_on_workers($$
    SELECT ROW(alias,dictionary) FROM ts_debug('text_search.french_noaccent', 'un chou-fleur') WHERE alias = 'asciihword' LIMIT 1;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |          result
---------------------------------------------------------------------
 localhost |    57637 | t       | (asciihword,french_stem)
 localhost |    57638 | t       | (asciihword,french_stem)
(2 rows)

-- drop a mapping
ALTER TEXT SEARCH CONFIGURATION french_noaccent DROP MAPPING FOR asciihword;
SELECT * FROM run_command_on_workers($$
    SELECT ROW(alias,dictionary) FROM ts_debug('text_search.french_noaccent', 'un chou-fleur') WHERE alias = 'asciihword' LIMIT 1;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |    result
---------------------------------------------------------------------
 localhost |    57637 | t       | (asciihword,)
 localhost |    57638 | t       | (asciihword,)
(2 rows)

-- also with exists, doesn't change anything, but should not error
ALTER TEXT SEARCH CONFIGURATION french_noaccent DROP MAPPING IF EXISTS FOR asciihword;
NOTICE:  mapping for token type "asciihword" does not exist, skipping
-- Comment on a text search configuration
COMMENT ON TEXT SEARCH CONFIGURATION french_noaccent IS 'a text configuration that is butchered to test all edge cases';
SELECT * FROM run_command_on_workers($$
    SELECT obj_description('text_search.french_noaccent'::regconfig);
$$) ORDER BY 1,2;
 nodename  | nodeport | success |                            result
---------------------------------------------------------------------
 localhost |    57637 | t       | a text configuration that is butchered to test all edge cases
 localhost |    57638 | t       | a text configuration that is butchered to test all edge cases
(2 rows)

-- Remove a comment
COMMENT ON TEXT SEARCH CONFIGURATION french_noaccent IS NULL;
SELECT * FROM run_command_on_workers($$
    SELECT obj_description('text_search.french_noaccent'::regconfig);
$$) ORDER BY 1,2;
 nodename  | nodeport | success | result
---------------------------------------------------------------------
 localhost |    57637 | t       |
 localhost |    57638 | t       |
(2 rows)

-- verify adding 2 dictionaries for two tokes at once
ALTER TEXT SEARCH CONFIGURATION french_noaccent DROP MAPPING IF EXISTS FOR asciiword, asciihword;
NOTICE:  mapping for token type "asciihword" does not exist, skipping
ALTER TEXT SEARCH CONFIGURATION french_noaccent ADD MAPPING FOR asciiword, asciihword WITH french_stem, dutch_stem;
SELECT * FROM run_command_on_workers($$
    SELECT ROW(alias,dictionaries) FROM ts_debug('text_search.french_noaccent', 'un chou-fleur') WHERE alias = 'asciiword' LIMIT 1;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |                 result
---------------------------------------------------------------------
 localhost |    57637 | t       | (asciiword,"{french_stem,dutch_stem}")
 localhost |    57638 | t       | (asciiword,"{french_stem,dutch_stem}")
(2 rows)

SELECT * FROM run_command_on_workers($$
    SELECT ROW(alias,dictionaries) FROM ts_debug('text_search.french_noaccent', 'un chou-fleur') WHERE alias = 'asciihword' LIMIT 1;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |                 result
---------------------------------------------------------------------
 localhost |    57637 | t       | (asciihword,"{french_stem,dutch_stem}")
 localhost |    57638 | t       | (asciihword,"{french_stem,dutch_stem}")
(2 rows)

--verify we can drop cascade a configuration that is in use
-- verify it is in use
DROP TEXT SEARCH CONFIGURATION text_search.french_noaccent;
ERROR:  cannot drop text search configuration french_noaccent because other objects depend on it
DETAIL:  index t2_search_name depends on text search configuration french_noaccent
HINT:  Use DROP ... CASCADE to drop the dependent objects too.
-- drop cascade
DROP TEXT SEARCH CONFIGURATION text_search.french_noaccent CASCADE;
NOTICE:  drop cascades to index t2_search_name
-- verify the configuration is dropped from the workers
SELECT * FROM run_command_on_workers($$ SELECT 'text_search.french_noaccent'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |                                     result
---------------------------------------------------------------------
 localhost |    57637 | f       | ERROR:  text search configuration "text_search.french_noaccent" does not exist
 localhost |    57638 | f       | ERROR:  text search configuration "text_search.french_noaccent" does not exist
(2 rows)

SET client_min_messages TO 'warning';
CREATE ROLE text_search_owner;
RESET client_min_messages;
CREATE TEXT SEARCH CONFIGURATION changed_owner ( PARSER = default );
SELECT * FROM run_command_on_workers($$
    SELECT cfgowner::regrole
      FROM pg_ts_config
     WHERE oid = 'text_search.changed_owner'::regconfig;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |  result
---------------------------------------------------------------------
 localhost |    57637 | t       | postgres
 localhost |    57638 | t       | postgres
(2 rows)

ALTER TEXT SEARCH CONFIGURATION changed_owner OWNER TO text_search_owner;
SELECT * FROM run_command_on_workers($$
    SELECT cfgowner::regrole
      FROM pg_ts_config
     WHERE oid = 'text_search.changed_owner'::regconfig;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |      result
---------------------------------------------------------------------
 localhost |    57637 | t       | text_search_owner
 localhost |    57638 | t       | text_search_owner
(2 rows)

-- redo test with propagating object after it was created and changed of owner
SET citus.enable_ddl_propagation TO off;
CREATE TEXT SEARCH CONFIGURATION changed_owner2 ( PARSER = default );
ALTER TEXT SEARCH CONFIGURATION changed_owner2 OWNER TO text_search_owner;
RESET citus.enable_ddl_propagation;
-- verify object doesn't exist before propagating
SELECT * FROM run_command_on_workers($$ SELECT 'text_search.changed_owner2'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |                                    result
---------------------------------------------------------------------
 localhost |    57637 | f       | ERROR:  text search configuration "text_search.changed_owner2" does not exist
 localhost |    57638 | f       | ERROR:  text search configuration "text_search.changed_owner2" does not exist
(2 rows)

-- distribute configuration
CREATE TABLE t3(id int, name text);
CREATE INDEX t3_search_name ON t3 USING gin (to_tsvector('text_search.changed_owner2'::regconfig, (COALESCE(name, ''::character varying))::text));
SELECT create_distributed_table('t3', 'name');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- verify config owner
SELECT * FROM run_command_on_workers($$
    SELECT cfgowner::regrole
      FROM pg_ts_config
     WHERE oid = 'text_search.changed_owner2'::regconfig;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |      result
---------------------------------------------------------------------
 localhost |    57637 | t       | text_search_owner
 localhost |    57638 | t       | text_search_owner
(2 rows)

-- rename tests
CREATE TEXT SEARCH CONFIGURATION change_name ( PARSER = default );
SELECT * FROM run_command_on_workers($$ -- verify the name exists on the worker
    SELECT 'text_search.change_name'::regconfig;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |         result
---------------------------------------------------------------------
 localhost |    57637 | t       | text_search.change_name
 localhost |    57638 | t       | text_search.change_name
(2 rows)

ALTER TEXT SEARCH CONFIGURATION change_name RENAME TO changed_name;
SELECT * FROM run_command_on_workers($$ -- verify the name exists on the worker
    SELECT 'text_search.changed_name'::regconfig;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |          result
---------------------------------------------------------------------
 localhost |    57637 | t       | text_search.changed_name
 localhost |    57638 | t       | text_search.changed_name
(2 rows)

-- test move of schema
CREATE TEXT SEARCH CONFIGURATION change_schema ( PARSER = default );
SELECT * FROM run_command_on_workers($$ -- verify the name exists on the worker
    SELECT 'text_search.change_schema'::regconfig;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |          result
---------------------------------------------------------------------
 localhost |    57637 | t       | text_search.change_schema
 localhost |    57638 | t       | text_search.change_schema
(2 rows)

ALTER TEXT SEARCH CONFIGURATION change_schema SET SCHEMA text_search2;
SELECT * FROM run_command_on_workers($$ -- verify the name exists on the worker
    SELECT 'text_search2.change_schema'::regconfig;
$$) ORDER BY 1,2;
 nodename  | nodeport | success |           result
---------------------------------------------------------------------
 localhost |    57637 | t       | text_search2.change_schema
 localhost |    57638 | t       | text_search2.change_schema
(2 rows)

-- verify we get an error that the configuration change_schema is not found, even though the object address will be
-- found in its new schema, and is distributed
ALTER TEXT SEARCH CONFIGURATION change_schema SET SCHEMA text_search2;
ERROR:  text search configuration "change_schema" does not exist
-- should tell us that text_search.does_not_exist does not exist, covers a complex edgecase
-- in resolving the object address
ALTER TEXT SEARCH CONFIGURATION text_search.does_not_exist SET SCHEMA text_search2;
ERROR:  text search configuration "text_search.does_not_exist" does not exist
-- verify edgecases in deparsers
CREATE TEXT SEARCH CONFIGURATION config1 ( PARSER = default );
CREATE TEXT SEARCH CONFIGURATION config2 ( PARSER = default );
SET citus.enable_ddl_propagation TO off;
CREATE TEXT SEARCH CONFIGURATION config3 ( PARSER = default );
RESET citus.enable_ddl_propagation;
-- verify config1, config2 exist on workers, config3 not
SELECT * FROM run_command_on_workers($$ SELECT 'text_search.config1'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |       result
---------------------------------------------------------------------
 localhost |    57637 | t       | text_search.config1
 localhost |    57638 | t       | text_search.config1
(2 rows)

SELECT * FROM run_command_on_workers($$ SELECT 'text_search.config2'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |       result
---------------------------------------------------------------------
 localhost |    57637 | t       | text_search.config2
 localhost |    57638 | t       | text_search.config2
(2 rows)

SELECT * FROM run_command_on_workers($$ SELECT 'text_search.config3'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |                                 result
---------------------------------------------------------------------
 localhost |    57637 | f       | ERROR:  text search configuration "text_search.config3" does not exist
 localhost |    57638 | f       | ERROR:  text search configuration "text_search.config3" does not exist
(2 rows)

-- DROP all config's, only 1&2 are distributed, they should propagate well to remotes
DROP TEXT SEARCH CONFIGURATION config1, config2, config3;
-- verify all existing ones have been removed (checking config3 for consistency)
SELECT * FROM run_command_on_workers($$ SELECT 'text_search.config1'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |                                 result
---------------------------------------------------------------------
 localhost |    57637 | f       | ERROR:  text search configuration "text_search.config1" does not exist
 localhost |    57638 | f       | ERROR:  text search configuration "text_search.config1" does not exist
(2 rows)

SELECT * FROM run_command_on_workers($$ SELECT 'text_search.config2'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |                                 result
---------------------------------------------------------------------
 localhost |    57637 | f       | ERROR:  text search configuration "text_search.config2" does not exist
 localhost |    57638 | f       | ERROR:  text search configuration "text_search.config2" does not exist
(2 rows)

SELECT * FROM run_command_on_workers($$ SELECT 'text_search.config3'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |                                 result
---------------------------------------------------------------------
 localhost |    57637 | f       | ERROR:  text search configuration "text_search.config3" does not exist
 localhost |    57638 | f       | ERROR:  text search configuration "text_search.config3" does not exist
(2 rows)

-- verify they are all removed locally
SELECT 1 FROM pg_ts_config WHERE cfgname = 'config1' AND cfgnamespace = 'text_search'::regnamespace;
 ?column?
---------------------------------------------------------------------
(0 rows)

SELECT 1 FROM pg_ts_config WHERE cfgname = 'config2' AND cfgnamespace = 'text_search'::regnamespace;
 ?column?
---------------------------------------------------------------------
(0 rows)

SELECT 1 FROM pg_ts_config WHERE cfgname = 'config3' AND cfgnamespace = 'text_search'::regnamespace;
 ?column?
---------------------------------------------------------------------
(0 rows)

-- verify that indexes created concurrently that would propagate a TEXT SEARCH CONFIGURATION object
SET citus.enable_ddl_propagation TO off;
CREATE TEXT SEARCH CONFIGURATION concurrent_index_config ( PARSER = default );
RESET citus.enable_ddl_propagation;
-- verify it doesn't exist on the workers
SELECT * FROM run_command_on_workers($$ SELECT 'text_search.concurrent_index_config'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |                                         result
---------------------------------------------------------------------
 localhost |    57637 | f       | ERROR:  text search configuration "text_search.concurrent_index_config" does not exist
 localhost |    57638 | f       | ERROR:  text search configuration "text_search.concurrent_index_config" does not exist
(2 rows)

-- create distributed table that then concurrently would have an index created.
CREATE TABLE t4(id int, name text);
SELECT create_distributed_table('t4', 'name');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE INDEX CONCURRENTLY t4_search_name ON t4 USING gin (to_tsvector('text_search.concurrent_index_config'::regconfig, (COALESCE(name, ''::character varying))::text));
-- now the configuration should be on the worker, and the above index creation shouldn't have failed.
SELECT * FROM run_command_on_workers($$ SELECT 'text_search.concurrent_index_config'::regconfig; $$) ORDER BY 1,2;
 nodename  | nodeport | success |               result
---------------------------------------------------------------------
 localhost |    57637 | t       | text_search.concurrent_index_config
 localhost |    57638 | t       | text_search.concurrent_index_config
(2 rows)

-- verify the objid is correctly committed locally due to the somewhat convoluted commit and new transaction starting when creating an index concurrently
SELECT pg_catalog.pg_identify_object_as_address(classid, objid, objsubid)
  FROM pg_catalog.pg_dist_object
 WHERE classid = 3602 AND objid = 'text_search.concurrent_index_config'::regconfig::oid;
                      pg_identify_object_as_address
---------------------------------------------------------------------
 ("text search configuration","{text_search,concurrent_index_config}",{})
(1 row)

-- verify old text search configurations get renamed if they are not the same as the newly propagated configuration.
-- We do this by creating configurations on the workers as a copy from a different existing catalog.
SELECT * FROM run_command_on_workers($$
    set citus.enable_metadata_sync TO off;
    CREATE TEXT SEARCH CONFIGURATION text_search.manually_created_wrongly ( copy = dutch );
    reset citus.enable_metadata_sync;
$$) ORDER BY 1,2;
 nodename  | nodeport | success | result
---------------------------------------------------------------------
 localhost |    57637 | t       | SET
 localhost |    57638 | t       | SET
(2 rows)

CREATE TEXT SEARCH CONFIGURATION text_search.manually_created_wrongly ( copy = french );
-- now we expect manually_created_wrongly(citus_backup_XXX) to show up when querying the configurations
SELECT * FROM run_command_on_workers($$
    SELECT array_agg(cfgname ORDER BY cfgname) FROM pg_ts_config WHERE cfgname LIKE 'manually_created_wrongly%';
$$) ORDER BY 1,2;
 nodename  | nodeport | success |                               result
---------------------------------------------------------------------
 localhost |    57637 | t       | {manually_created_wrongly,manually_created_wrongly(citus_backup_0)}
 localhost |    57638 | t       | {manually_created_wrongly,manually_created_wrongly(citus_backup_0)}
(2 rows)

-- verify the objects get reused appropriately when the specification is the same
SELECT * FROM run_command_on_workers($$
    set citus.enable_metadata_sync TO off;
    CREATE TEXT SEARCH CONFIGURATION text_search.manually_created_correct ( copy = french );
    reset citus.enable_metadata_sync;
$$) ORDER BY 1,2;
 nodename  | nodeport | success | result
---------------------------------------------------------------------
 localhost |    57637 | t       | SET
 localhost |    57638 | t       | SET
(2 rows)

CREATE TEXT SEARCH CONFIGURATION text_search.manually_created_correct ( copy = french );
-- now we don't expect manually_created_correct(citus_backup_XXX) to show up when querying the configurations as the
-- original one is reused
SELECT * FROM run_command_on_workers($$
    SELECT array_agg(cfgname ORDER BY cfgname) FROM pg_ts_config WHERE cfgname LIKE 'manually_created_correct%';
$$) ORDER BY 1,2;
 nodename  | nodeport | success |           result
---------------------------------------------------------------------
 localhost |    57637 | t       | {manually_created_correct}
 localhost |    57638 | t       | {manually_created_correct}
(2 rows)

CREATE SCHEMA "Text Search Requiring Quote's";
CREATE TEXT SEARCH CONFIGURATION "Text Search Requiring Quote's"."Quoted Config Name" ( parser = default );
CREATE TABLE t5(id int, name text);
CREATE INDEX t5_search_name ON t5 USING gin (to_tsvector('"Text Search Requiring Quote''s"."Quoted Config Name"'::regconfig, (COALESCE(name, ''::character varying))::text));
SELECT create_distributed_table('t5', 'name');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- make sure partial indices propagate their dependencies
-- first have a TEXT SEARCH CONFIGURATION that is not distributed
SET citus.enable_ddl_propagation TO off;
CREATE TEXT SEARCH CONFIGURATION partial_index_test_config ( parser = default );
RESET citus.enable_ddl_propagation;
CREATE TABLE sensors(
    measureid integer,
    eventdatetime date,
    measure_data jsonb,
    name text,
    PRIMARY KEY (measureid, eventdatetime, measure_data)
) PARTITION BY RANGE(eventdatetime);
CREATE TABLE sensors_a_partition PARTITION OF sensors FOR VALUES FROM ('2000-01-01') TO ('2020-01-01');
CREATE INDEX sensors_search_name ON sensors USING gin (to_tsvector('partial_index_test_config'::regconfig, (COALESCE(name, ''::character varying))::text));
SELECT create_distributed_table('sensors', 'measureid');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

--  create a new dictionary from scratch
CREATE TEXT SEARCH DICTIONARY my_english_dict (
    template = snowball,
    language = english,
    stopwords = english
);
-- verify that the dictionary definition is the same in all nodes
SELECT result FROM run_command_on_all_nodes($$
    SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
    FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
    WHERE dictname = 'my_english_dict';
$$);
                                            result
---------------------------------------------------------------------
 (my_english_dict,text_search,postgres,snowball,"language = 'english', stopwords = 'english'")
 (my_english_dict,text_search,postgres,snowball,"language = 'english', stopwords = 'english'")
 (my_english_dict,text_search,postgres,snowball,"language = 'english', stopwords = 'english'")
(3 rows)

-- use the new dictionary in a configuration mapping
CREATE TEXT SEARCH CONFIGURATION my_english_config ( COPY = english );
ALTER TEXT SEARCH CONFIGURATION my_english_config ALTER MAPPING FOR asciiword WITH my_english_dict;
-- verify that the dictionary is available on the worker nodes
SELECT result FROM run_command_on_all_nodes($$
    SELECT ROW(alias,dictionary) FROM ts_debug('text_search.my_english_config', 'The Brightest supernovaes') WHERE alias = 'asciiword' LIMIT 1;
$$);
                 result
---------------------------------------------------------------------
 (asciiword,text_search.my_english_dict)
 (asciiword,text_search.my_english_dict)
 (asciiword,text_search.my_english_dict)
(3 rows)

-- comment on a text search dictionary
COMMENT ON TEXT SEARCH DICTIONARY my_english_dict IS 'a text search dictionary that is butchered to test all edge cases';
SELECT result FROM run_command_on_all_nodes($$
    SELECT obj_description('text_search.my_english_dict'::regdictionary);
$$);
                              result
---------------------------------------------------------------------
 a text search dictionary that is butchered to test all edge cases
 a text search dictionary that is butchered to test all edge cases
 a text search dictionary that is butchered to test all edge cases
(3 rows)

-- remove a comment
COMMENT ON TEXT SEARCH DICTIONARY my_english_dict IS NULL;
SELECT result FROM run_command_on_all_nodes($$
    SELECT obj_description('text_search.my_english_dict'::regdictionary);
$$);
 result
---------------------------------------------------------------------



(3 rows)

-- test various ALTER TEXT SEARCH DICTIONARY commands
ALTER TEXT SEARCH DICTIONARY my_english_dict RENAME TO my_turkish_dict;
ALTER TEXT SEARCH DICTIONARY my_turkish_dict (language = turkish, stopwords);
ALTER TEXT SEARCH DICTIONARY my_turkish_dict OWNER TO text_search_owner;
ALTER TEXT SEARCH DICTIONARY my_turkish_dict SET SCHEMA "Text Search Requiring Quote's";
-- verify that the dictionary definition is the same in all nodes
SELECT result FROM run_command_on_all_nodes($$
    SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
    FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
    WHERE dictname = 'my_turkish_dict';
$$);
                                                 result
---------------------------------------------------------------------
 (my_turkish_dict,"""Text Search Requiring Quote's""",text_search_owner,snowball,"language = 'turkish'")
 (my_turkish_dict,"""Text Search Requiring Quote's""",text_search_owner,snowball,"language = 'turkish'")
 (my_turkish_dict,"""Text Search Requiring Quote's""",text_search_owner,snowball,"language = 'turkish'")
(3 rows)

-- verify that the configuration dictionary is changed in all nodes
SELECT result FROM run_command_on_all_nodes($$
    SELECT ROW(alias,dictionary) FROM ts_debug('text_search.my_english_config', 'The Brightest supernovaes') WHERE alias = 'asciiword' LIMIT 1;
$$);
                             result
---------------------------------------------------------------------
 (asciiword,"""Text Search Requiring Quote's"".my_turkish_dict")
 (asciiword,"""Text Search Requiring Quote's"".my_turkish_dict")
 (asciiword,"""Text Search Requiring Quote's"".my_turkish_dict")
(3 rows)

-- before testing drops, check that the dictionary exists on all nodes
SELECT result FROM run_command_on_all_nodes($$
    SELECT '"Text Search Requiring Quote''s".my_turkish_dict'::regdictionary;
$$);
                     result
---------------------------------------------------------------------
 "Text Search Requiring Quote's".my_turkish_dict
 "Text Search Requiring Quote's".my_turkish_dict
 "Text Search Requiring Quote's".my_turkish_dict
(3 rows)

ALTER TEXT SEARCH DICTIONARY "Text Search Requiring Quote's".my_turkish_dict SET SCHEMA text_search;
-- verify that we can drop the dictionary only with cascade option
DROP TEXT SEARCH DICTIONARY my_turkish_dict;
ERROR:  cannot drop text search dictionary my_turkish_dict because other objects depend on it
DETAIL:  text search configuration my_english_config depends on text search dictionary my_turkish_dict
HINT:  Use DROP ... CASCADE to drop the dependent objects too.
DROP TEXT SEARCH DICTIONARY my_turkish_dict CASCADE;
NOTICE:  drop cascades to text search configuration my_english_config
-- verify that it is dropped now
SELECT result FROM run_command_on_all_nodes($$
    SELECT 'my_turkish_dict'::regdictionary;
$$);
                             result
---------------------------------------------------------------------
 ERROR:  text search dictionary "my_turkish_dict" does not exist
 ERROR:  text search dictionary "my_turkish_dict" does not exist
 ERROR:  text search dictionary "my_turkish_dict" does not exist
(3 rows)

-- test different templates that are used in dictionaries
CREATE TEXT SEARCH DICTIONARY simple_dict (
    TEMPLATE = pg_catalog.simple,
    STOPWORDS = english,
    accept = false
);
SELECT COUNT(DISTINCT result)=1 FROM run_command_on_all_nodes($$
    SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
    FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
    WHERE dictname = 'simple_dict';
$$);
 ?column?
---------------------------------------------------------------------
 t
(1 row)

CREATE TEXT SEARCH DICTIONARY synonym_dict (
    template=synonym,
    synonyms='synonym_sample',
    casesensitive=1
);
SELECT COUNT(DISTINCT result)=1 FROM run_command_on_all_nodes($$
    SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
    FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
    WHERE dictname = 'synonym_dict';
$$);
 ?column?
---------------------------------------------------------------------
 t
(1 row)

CREATE TEXT SEARCH DICTIONARY thesaurus_dict (
    TEMPLATE = thesaurus,
    DictFile = thesaurus_sample,
    Dictionary = pg_catalog.english_stem
);
SELECT COUNT(DISTINCT result)=1 FROM run_command_on_all_nodes($$
    SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
    FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
    WHERE dictname = 'thesaurus_dict';
$$);
 ?column?
---------------------------------------------------------------------
 t
(1 row)

CREATE TEXT SEARCH DICTIONARY ispell_dict (
    TEMPLATE = ispell,
    DictFile = ispell_sample,
    AffFile = ispell_sample,
    Stopwords = english
);
SELECT COUNT(DISTINCT result)=1 FROM run_command_on_all_nodes($$
    SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
    FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
    WHERE dictname = 'ispell_dict';
$$);
 ?column?
---------------------------------------------------------------------
 t
(1 row)

CREATE TEXT SEARCH DICTIONARY snowball_dict (
    TEMPLATE = snowball,
    Language = english,
    StopWords = english
);
SELECT COUNT(DISTINCT result)=1 FROM run_command_on_all_nodes($$
    SELECT ROW(dictname, dictnamespace::regnamespace, dictowner::regrole, tmplname, dictinitoption)
    FROM pg_ts_dict d JOIN pg_ts_template t ON ( d.dicttemplate = t.oid )
    WHERE dictname = 'snowball_dict';
$$);
 ?column?
---------------------------------------------------------------------
 t
(1 row)

-- will skip trying to propagate the text search configuration due to temp schema
CREATE TEXT SEARCH CONFIGURATION pg_temp.temp_text_search_config ( parser = default );
WARNING:  "text search configuration pg_temp_xxx.temp_text_search_config" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL:  "text search configuration pg_temp_xxx.temp_text_search_config" will be created only locally
-- will skip trying to propagate the text search dictionary due to temp schema
CREATE TEXT SEARCH DICTIONARY pg_temp.temp_text_search_dict (
    template = snowball,
    language = english,
    stopwords = english
);
WARNING:  "text search dictionary pg_temp_xxx.temp_text_search_dict" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL:  "text search dictionary pg_temp_xxx.temp_text_search_dict" will be created only locally
SET client_min_messages TO 'warning';
DROP SCHEMA text_search, text_search2, "Text Search Requiring Quote's" CASCADE;
DROP ROLE text_search_owner;
