CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(_js jsonb) RETURNS text[] LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS $$SELECT CASE WHEN _js = 'null' THEN NULL ELSE ARRAY(SELECT jsonb_array_elements_text(_js)) END$$; -- Creates new dictionary from remote instance. CREATE OR REPLACE FUNCTION dictionary_remote_new (_dictionary JSONB, OUT _dictionary_id dictionary.id%type) LANGUAGE plpgsql AS $$ DECLARE _domain_secondary_names JSONB[] := jsonb_array_to_text_array(_dictionary -> 'domain_secondary_names')::JSONB[]; _domain_secondary_name JSONB; _domain_secondary_id domain_secondary.id%type; _language_codes VARCHAR[] := jsonb_array_to_text_array(_dictionary -> 'language_codes'); _language_id language.id%type; BEGIN -- Disable trigger from updating time_modified. SET LOCAL term_portal.skip_update_time_modified TO 'TRUE'; -- Create new dictionary. INSERT INTO dictionary ( name_sl, name_sl_short, name_en, author, issn, domain_primary_id, time_created, time_modified, time_content_modified, status, description, entries_have_domain_labels, entries_have_label, entries_have_definition, entries_have_synonyms, entries_have_links, entries_have_other, entries_have_foreign_languages, entries_have_foreign_definitions, entries_have_foreign_synonyms, entries_have_images, entries_have_audio, entries_have_videos, entries_have_terminology_review_flag, entries_have_language_review_flag ) VALUES ( _dictionary ->> 'name_sl', _dictionary ->> 'name_sl_short', _dictionary ->> 'name_en', jsonb_array_to_text_array(_dictionary -> 'author'), _dictionary ->> 'issn', COALESCE( (SELECT id FROM domain_primary WHERE name_sl = _dictionary ->> 'domain_primary_name_sl'), (SELECT id FROM domain_primary WHERE name_sl = 'Neopredeljeno') ), (_dictionary ->> 'time_created')::TIMESTAMPTZ, (_dictionary ->> 'time_modified')::TIMESTAMPTZ, (_dictionary ->> 'time_content_modified')::TIMESTAMPTZ, 'published', _dictionary ->> 'description', (_dictionary ->> 'entries_have_domain_labels')::BOOL, (_dictionary ->> 'entries_have_label')::BOOL, (_dictionary ->> 'entries_have_definition')::BOOL, (_dictionary ->> 'entries_have_synonyms')::BOOL, (_dictionary ->> 'entries_have_links')::BOOL, (_dictionary ->> 'entries_have_other')::BOOL, (_dictionary ->> 'entries_have_foreign_languages')::BOOL, (_dictionary ->> 'entries_have_foreign_definitions')::BOOL, (_dictionary ->> 'entries_have_foreign_synonyms')::BOOL, (_dictionary ->> 'entries_have_images')::BOOL, (_dictionary ->> 'entries_have_audio')::BOOL, (_dictionary ->> 'entries_have_videos')::BOOL, (_dictionary ->> 'entries_have_terminology_review_flag')::BOOL, (_dictionary ->> 'entries_have_language_review_flag')::BOOL ) RETURNING id INTO _dictionary_id; -- Create and/or associate secondary domains. FOREACH _domain_secondary_name IN ARRAY _domain_secondary_names LOOP SELECT id INTO _domain_secondary_id FROM domain_secondary WHERE name_sl = _domain_secondary_name ->> 'name_sl' AND name_en = _domain_secondary_name ->> 'name_en'; IF _domain_secondary_id IS NULL THEN INSERT INTO domain_secondary (name_sl, name_en) VALUES (_domain_secondary_name ->> 'name_sl', _domain_secondary_name ->> 'name_en') RETURNING id INTO _domain_secondary_id; END IF; INSERT INTO dictionary_domain_secondary (dictionary_id, domain_secondary_id) VALUES (_dictionary_id, _domain_secondary_id); END LOOP; -- Associate languages. FOR i IN 1..COALESCE(array_length(_language_codes, 1), 0) LOOP SELECT id INTO _language_id FROM language WHERE code = _language_codes[i]; IF _language_id IS NOT NULL THEN INSERT INTO dictionary_language (dictionary_id, language_id, selection_order) VALUES (_dictionary_id, _language_id, i); END IF; END LOOP; END $$; -- Updates dictionary from remote instance. CREATE OR REPLACE FUNCTION dictionary_remote_update (_dictionary_id dictionary.id%type, _dictionary JSONB) RETURNS void LANGUAGE plpgsql AS $$ DECLARE _domain_secondary_names JSONB[] := jsonb_array_to_text_array(_dictionary -> 'domain_secondary_names')::JSONB[]; _domain_secondary_name JSONB; _domain_secondary_id domain_secondary.id%type; _language_codes VARCHAR[] := jsonb_array_to_text_array(_dictionary -> 'language_codes'); _language_id language.id%type; BEGIN -- Disable trigger from updating time_modified. SET LOCAL term_portal.skip_update_time_modified TO 'TRUE'; -- Update dictionary table. UPDATE dictionary SET name_sl = _dictionary ->> 'name_sl', name_sl_short = _dictionary ->> 'name_sl_short', name_en = _dictionary ->> 'name_en', author = jsonb_array_to_text_array(_dictionary -> 'author'), issn = _dictionary ->> 'issn', domain_primary_id = COALESCE( (SELECT id FROM domain_primary WHERE name_sl = _dictionary ->> 'domain_primary_name_sl'), (SELECT id FROM domain_primary WHERE name_sl = 'Neopredeljeno') ), time_created = (_dictionary ->> 'time_created')::TIMESTAMPTZ, time_modified = (_dictionary ->> 'time_modified')::TIMESTAMPTZ, time_content_modified = (_dictionary ->> 'time_content_modified')::TIMESTAMPTZ, description = _dictionary ->> 'description', entries_have_domain_labels = (_dictionary ->> 'entries_have_domain_labels')::BOOL, entries_have_label = (_dictionary ->> 'entries_have_label')::BOOL, entries_have_definition = (_dictionary ->> 'entries_have_definition')::BOOL, entries_have_synonyms = (_dictionary ->> 'entries_have_synonyms')::BOOL, entries_have_links =(_dictionary ->> 'entries_have_links')::BOOL, entries_have_other = (_dictionary ->> 'entries_have_other')::BOOL, entries_have_foreign_languages = (_dictionary ->> 'entries_have_foreign_languages')::BOOL, entries_have_foreign_definitions = (_dictionary ->> 'entries_have_foreign_definitions')::BOOL, entries_have_foreign_synonyms = (_dictionary ->> 'entries_have_foreign_synonyms')::BOOL, entries_have_images = (_dictionary ->> 'entries_have_images')::BOOL, entries_have_audio = (_dictionary ->> 'entries_have_audio')::BOOL, entries_have_videos = (_dictionary ->> 'entries_have_videos')::BOOL, entries_have_terminology_review_flag = (_dictionary ->> 'entries_have_terminology_review_flag')::BOOL, entries_have_language_review_flag = (_dictionary ->> 'entries_have_language_review_flag')::BOOL WHERE id = _dictionary_id; -- Delete all existing secondary domain associations. DELETE FROM dictionary_domain_secondary WHERE dictionary_id = _dictionary_id; -- Create and/or associate new secondary domains, if any. FOREACH _domain_secondary_name IN ARRAY _domain_secondary_names LOOP SELECT id INTO _domain_secondary_id FROM domain_secondary WHERE name_sl = _domain_secondary_name ->> 'name_sl' AND name_en = _domain_secondary_name ->> 'name_en'; IF _domain_secondary_id IS NULL THEN INSERT INTO domain_secondary (name_sl, name_en) VALUES (_domain_secondary_name ->> 'name_sl', _domain_secondary_name ->> 'name_en') RETURNING id INTO _domain_secondary_id; END IF; INSERT INTO dictionary_domain_secondary (dictionary_id, domain_secondary_id) VALUES (_dictionary_id, _domain_secondary_id); END LOOP; -- Delete all existing language associations. DELETE FROM dictionary_language WHERE dictionary_id = _dictionary_id; -- Associate new languages, if any. FOR i IN 1..COALESCE(array_length(_language_codes, 1), 0) LOOP SELECT id INTO _language_id FROM language WHERE code = _language_codes[i]; IF _language_id IS NOT NULL THEN INSERT INTO dictionary_language (dictionary_id, language_id, selection_order) VALUES (_dictionary_id, _language_id, i); END IF; END LOOP; END $$; -- Sync remote dictionaries (but not entries). CREATE OR REPLACE FUNCTION sync_remote_dictionaries ( _linked_portal_id linked_portal.id%type, _dictionaries JSONB[] ) RETURNS void LANGUAGE PLPGSQL AS $$ DECLARE _dictionary JSONB; _source_dictionary_id linked_dictionary.source_dictionary_id%type; _target_dictionary_id linked_dictionary.target_dictionary_id%type; _remote_dictionary_ids VARCHAR[] := ARRAY[]::VARCHAR[]; _time_modified_old dictionary.time_modified%type; _time_modified_new dictionary.time_modified%type; BEGIN -- For each of the provided dictionaries: create | update | skip. FOREACH _dictionary IN ARRAY _dictionaries LOOP _source_dictionary_id := _dictionary ->> 'id'; _remote_dictionary_ids = _remote_dictionary_ids || _source_dictionary_id; SELECT target_dictionary_id INTO _target_dictionary_id FROM linked_dictionary WHERE source_dictionary_id = _source_dictionary_id; -- Check if dictionary doesn't exist yet. IF _target_dictionary_id IS NULL THEN -- Create new dictionary. SELECT dictionary_remote_new(_dictionary) INTO _target_dictionary_id; -- Create new linked dictionary. INSERT INTO linked_dictionary(name, linked_portal_id, source_dictionary_id, target_dictionary_id) VALUES (_dictionary ->> 'name_sl', _linked_portal_id, _source_dictionary_id, _target_dictionary_id); ELSE -- Dictionary already exists. -- Compare modification times to determine if update is required. SELECT time_modified INTO _time_modified_old FROM dictionary WHERE id = _target_dictionary_id; _time_modified_new := _dictionary ->> 'time_modified'; -- Skip this dictionary, if modification times match. CONTINUE WHEN _time_modified_new = _time_modified_old; -- Update dictionary. PERFORM dictionary_remote_update(_target_dictionary_id, _dictionary); -- Update linked dictionary. UPDATE linked_dictionary SET name = _dictionary ->> 'name_sl' WHERE target_dictionary_id = _target_dictionary_id; END IF; END LOOP; -- Delete any existing dictionaries that were not provided to be synced. DELETE FROM dictionary WHERE id IN ( SELECT target_dictionary_id FROM linked_dictionary WHERE linked_portal_id = _linked_portal_id AND source_dictionary_id <> ALL (_remote_dictionary_ids) ); END $$;