From 199e53b7a17a0864e5675651dc453fe3b2cec431 Mon Sep 17 00:00:00 2001 From: Artem Yevsiukov Date: Tue, 21 Nov 2023 17:18:17 +0200 Subject: [PATCH] DPE-2764 Add new extensions/plugins (#251) * Add new extensions/plugins * fix tests --- config.yaml | 92 +++++++++++++++++++++ src/charm.py | 3 + src/config.py | 23 ++++++ tests/integration/test_plugins.py | 129 ++++++++++++++++++------------ tests/unit/test_charm.py | 77 +++++++++++++++++- 5 files changed, 270 insertions(+), 54 deletions(-) diff --git a/config.yaml b/config.yaml index 781c213e62..374399cacf 100644 --- a/config.yaml +++ b/config.yaml @@ -123,6 +123,98 @@ options: default: false type: boolean description: Enable unaccent extension + plugin_bloom_enable: + default: false + type: boolean + description: Enable bloom extension + plugin_btree_gin_enable: + default: false + type: boolean + description: Enable btree_gin extension + plugin_btree_gist_enable: + default: false + type: boolean + description: Enable btree_gist extension + plugin_cube_enable: + default: false + type: boolean + description: Enable cube extension + plugin_dict_int_enable: + default: false + type: boolean + description: Enable dict_int extension + plugin_dict_xsyn_enable: + default: false + type: boolean + description: Enable dict_xsyn extension + plugin_earthdistance_enable: + default: false + type: boolean + description: Enable earthdistance extension + plugin_fuzzystrmatch_enable: + default: false + type: boolean + description: Enable fuzzystrmatch extension + plugin_intarray_enable: + default: false + type: boolean + description: Enable intarray extension + plugin_isn_enable: + default: false + type: boolean + description: Enable isn extension + plugin_lo_enable: + default: false + type: boolean + description: Enable lo extension + plugin_ltree_enable: + default: false + type: boolean + description: Enable ltree extension + plugin_old_snapshot_enable: + default: false + type: boolean + description: Enable old_snapshot extension + plugin_pg_freespacemap_enable: + default: false + type: boolean + description: Enable pg_freespacemap extension + plugin_pgrowlocks_enable: + default: false + type: boolean + description: Enable pgrowlocks extension + plugin_pgstattuple_enable: + default: false + type: boolean + description: Enable pgstattuple extension + plugin_pg_visibility_enable: + default: false + type: boolean + description: Enable pg_visibility extension + plugin_seg_enable: + default: false + type: boolean + description: Enable seg extension + plugin_tablefunc_enable: + default: false + type: boolean + description: Enable tablefunc extension + plugin_tcn_enable: + default: false + type: boolean + description: Enable tcn extension + plugin_tsm_system_rows_enable: + default: false + type: boolean + description: Enable tsm_system_rows extension + plugin_tsm_system_time_enable: + default: false + type: boolean + description: Enable tsm_system_time extension + plugin_uuid_ossp_enable: + default: false + type: boolean + description: Enable uuid_ossp extension profile: description: | Profile representing the scope of deployment, and used to tune resource allocation. diff --git a/src/charm.py b/src/charm.py index 999b3bb722..34dfe0754c 100755 --- a/src/charm.py +++ b/src/charm.py @@ -948,6 +948,7 @@ def enable_disable_extensions(self, database: str = None) -> None: database: optional database where to enable/disable the extension. """ original_status = self.unit.status + plugins_exception = {"uuid_ossp": '"uuid-ossp"'} extensions = {} # collect extensions for plugin in self.config.plugin_keys(): @@ -955,6 +956,8 @@ def enable_disable_extensions(self, database: str = None) -> None: # Enable or disable the plugin/extension. extension = "_".join(plugin.split("_")[1:-1]) + if extension in plugins_exception: + extension = plugins_exception[extension] extensions[extension] = enable self.unit.status = WaitingStatus("Updating extensions") try: diff --git a/src/config.py b/src/config.py index 795357815d..bc3f06c728 100644 --- a/src/config.py +++ b/src/config.py @@ -40,6 +40,29 @@ class CharmConfig(BaseConfigModel): plugin_pg_trgm_enable: bool plugin_plpython3u_enable: bool plugin_unaccent_enable: bool + plugin_bloom_enable: bool + plugin_btree_gin_enable: bool + plugin_btree_gist_enable: bool + plugin_cube_enable: bool + plugin_dict_int_enable: bool + plugin_dict_xsyn_enable: bool + plugin_earthdistance_enable: bool + plugin_fuzzystrmatch_enable: bool + plugin_intarray_enable: bool + plugin_isn_enable: bool + plugin_lo_enable: bool + plugin_ltree_enable: bool + plugin_old_snapshot_enable: bool + plugin_pg_freespacemap_enable: bool + plugin_pgrowlocks_enable: bool + plugin_pgstattuple_enable: bool + plugin_pg_visibility_enable: bool + plugin_seg_enable: bool + plugin_tablefunc_enable: bool + plugin_tcn_enable: bool + plugin_tsm_system_rows_enable: bool + plugin_tsm_system_time_enable: bool + plugin_uuid_ossp_enable: bool request_date_style: Optional[str] request_standard_conforming_strings: Optional[bool] request_time_zone: Optional[str] diff --git a/tests/integration/test_plugins.py b/tests/integration/test_plugins.py index 4a415015e9..884eed6eb0 100644 --- a/tests/integration/test_plugins.py +++ b/tests/integration/test_plugins.py @@ -24,6 +24,35 @@ PG_TRGM_EXTENSION_STATEMENT = "SELECT word_similarity('word', 'two words');" PLPYTHON3U_EXTENSION_STATEMENT = 'CREATE FUNCTION plpython_test() RETURNS varchar[] AS $$ return "hello" $$ LANGUAGE plpython3u;' UNACCENT_EXTENSION_STATEMENT = "SELECT ts_lexize('unaccent','Hôtel');" +BLOOM_EXTENSION_STATEMENT = ( + "CREATE TABLE tbloom_test (i int);CREATE INDEX btreeidx ON tbloom_test USING bloom (i);" +) +BTREEGIN_EXTENSION_STATEMENT = "CREATE TABLE btree_gin_test (a int4);CREATE INDEX btreeginidx ON btree_gin_test USING GIN (a);" +BTREEGIST_EXTENSION_STATEMENT = "CREATE TABLE btree_gist_test (a int4);CREATE INDEX btreegistidx ON btree_gist_test USING GIST (a);" +CUBE_EXTENSION_STATEMENT = "SELECT cube_inter('(0,-1),(1,1)', '(-2),(2)');" +DICTINT_EXTENSION_STATEMENT = "SELECT ts_lexize('intdict', '12345678');" +DICTXSYN_EXTENSION_STATEMENT = "SELECT ts_lexize('xsyn', 'word');" +EARTHDISTANCE_EXTENSION_STATEMENT = "SELECT earth_distance(ll_to_earth(-81.3927381, 30.2918842),ll_to_earth(-87.6473133, 41.8853881));" +FUZZYSTRMATCH_EXTENSION_STATEMENT = "SELECT soundex('hello world!');" +INTARRAY_EXTENSION_STATEMENT = "CREATE TABLE intarray_test (mid INT PRIMARY KEY, sections INT[]);SELECT intarray_test.mid FROM intarray_test WHERE intarray_test.sections @> '{1,2}';" +ISN_EXTENSION_STATEMENT = "SELECT isbn('978-0-393-04002-9');" +LO_EXTENSION_STATEMENT = "CREATE TABLE lo_test (value lo);" +LTREE_EXTENSION_STATEMENT = "CREATE TABLE ltree_test (path ltree);" +OLD_SNAPSHOT_EXTENSION_STATEMENT = "SELECT * from pg_old_snapshot_time_mapping();" +PG_FREESPACEMAP_EXTENSION_STATEMENT = ( + "CREATE TABLE pg_freespacemap_test (i int);SELECT * FROM pg_freespace('pg_freespacemap_test');" +) +PGROWLOCKS_EXTENSION_STATEMENT = ( + "CREATE TABLE pgrowlocks_test (i int);SELECT * FROM pgrowlocks('pgrowlocks_test');" +) +PGSTATTUPLE_EXTENSION_STATEMENT = "SELECT * FROM pgstattuple('pg_catalog.pg_proc');" +PG_VISIBILITY_EXTENSION_STATEMENT = "CREATE TABLE pg_visibility_test (i int);SELECT * FROM pg_visibility('pg_visibility_test'::regclass);" +SEG_EXTENSION_STATEMENT = "SELECT '10(+-)1'::seg as seg;" +TABLEFUNC_EXTENSION_STATEMENT = "SELECT * FROM normal_rand(1000, 5, 3);" +TCN_EXTENSION_STATEMENT = "CREATE TABLE tcn_test (i int);CREATE TRIGGER tcn_test_idx AFTER INSERT OR UPDATE OR DELETE ON tcn_test FOR EACH ROW EXECUTE FUNCTION TRIGGERED_CHANGE_NOTIFICATION();" +TSM_SYSTEM_ROWS_EXTENSION_STATEMENT = "CREATE TABLE tsm_system_rows_test (i int);SELECT * FROM tsm_system_rows_test TABLESAMPLE SYSTEM_ROWS(100);" +TSM_SYSTEM_TIME_EXTENSION_STATEMENT = "CREATE TABLE tsm_system_time_test (i int);SELECT * FROM tsm_system_time_test TABLESAMPLE SYSTEM_TIME(1000);" +UUID_OSSP_EXTENSION_STATEMENT = "SELECT uuid_nil();" @pytest.mark.abort_on_fail @@ -40,49 +69,65 @@ async def test_plugins(ops_test: OpsTest) -> None: ) await ops_test.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active", timeout=1000) + sql_tests = { + "plugin_citext_enable": CITEXT_EXTENSION_STATEMENT, + "plugin_debversion_enable": DEBVERSION_EXTENSION_STATEMENT, + "plugin_hstore_enable": HSTORE_EXTENSION_STATEMENT, + "plugin_pg_trgm_enable": PG_TRGM_EXTENSION_STATEMENT, + "plugin_plpython3u_enable": PLPYTHON3U_EXTENSION_STATEMENT, + "plugin_unaccent_enable": UNACCENT_EXTENSION_STATEMENT, + "plugin_bloom_enable": BLOOM_EXTENSION_STATEMENT, + "plugin_btree_gin_enable": BTREEGIN_EXTENSION_STATEMENT, + "plugin_btree_gist_enable": BTREEGIST_EXTENSION_STATEMENT, + "plugin_cube_enable": CUBE_EXTENSION_STATEMENT, + "plugin_dict_int_enable": DICTINT_EXTENSION_STATEMENT, + "plugin_dict_xsyn_enable": DICTXSYN_EXTENSION_STATEMENT, + "plugin_earthdistance_enable": EARTHDISTANCE_EXTENSION_STATEMENT, + "plugin_fuzzystrmatch_enable": FUZZYSTRMATCH_EXTENSION_STATEMENT, + "plugin_intarray_enable": INTARRAY_EXTENSION_STATEMENT, + "plugin_isn_enable": ISN_EXTENSION_STATEMENT, + "plugin_lo_enable": LO_EXTENSION_STATEMENT, + "plugin_ltree_enable": LTREE_EXTENSION_STATEMENT, + "plugin_old_snapshot_enable": OLD_SNAPSHOT_EXTENSION_STATEMENT, + "plugin_pg_freespacemap_enable": PG_FREESPACEMAP_EXTENSION_STATEMENT, + "plugin_pgrowlocks_enable": PGROWLOCKS_EXTENSION_STATEMENT, + "plugin_pgstattuple_enable": PGSTATTUPLE_EXTENSION_STATEMENT, + "plugin_pg_visibility_enable": PG_VISIBILITY_EXTENSION_STATEMENT, + "plugin_seg_enable": SEG_EXTENSION_STATEMENT, + "plugin_tablefunc_enable": TABLEFUNC_EXTENSION_STATEMENT, + "plugin_tcn_enable": TCN_EXTENSION_STATEMENT, + "plugin_tsm_system_rows_enable": TSM_SYSTEM_ROWS_EXTENSION_STATEMENT, + "plugin_tsm_system_time_enable": TSM_SYSTEM_TIME_EXTENSION_STATEMENT, + "plugin_uuid_ossp_enable": UUID_OSSP_EXTENSION_STATEMENT, + } + + def enable_disable_config(enabled: False): + config = {} + for plugin in sql_tests.keys(): + config[plugin] = f"{enabled}" + return config + # Check that the available plugins are disabled. primary = await get_primary(ops_test, f"{DATABASE_APP_NAME}/0") password = await get_password(ops_test, primary) address = get_unit_address(ops_test, primary) + + config = enable_disable_config(False) + await ops_test.model.applications[DATABASE_APP_NAME].set_config(config) + await ops_test.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active") + logger.info("checking that the plugins are disabled") with db_connect(host=address, password=password) as connection: connection.autocommit = True - - # Test citext extension disabled. - with pytest.raises(psycopg2.Error): - connection.cursor().execute(CITEXT_EXTENSION_STATEMENT) - - # Test debversion extension disabled. - with pytest.raises(psycopg2.Error): - connection.cursor().execute(DEBVERSION_EXTENSION_STATEMENT) - - # Test hstore extension disabled. - with pytest.raises(psycopg2.Error): - connection.cursor().execute(HSTORE_EXTENSION_STATEMENT) - - # Test pg_trgm extension disabled. - with pytest.raises(psycopg2.Error): - connection.cursor().execute(PG_TRGM_EXTENSION_STATEMENT) - - # Test PL/Python extension disabled. - with pytest.raises(psycopg2.Error): - connection.cursor().execute(PLPYTHON3U_EXTENSION_STATEMENT) - - # Test unaccent extension disabled. - with pytest.raises(psycopg2.Error): - connection.cursor().execute(UNACCENT_EXTENSION_STATEMENT) + for query in sql_tests.values(): + with pytest.raises(psycopg2.Error): + connection.cursor().execute(query) connection.close() # Enable the plugins. logger.info("enabling the plugins") - config = { - "plugin_citext_enable": "True", - "plugin_debversion_enable": "True", - "plugin_hstore_enable": "True", - "plugin_pg_trgm_enable": "True", - "plugin_plpython3u_enable": "True", - "plugin_unaccent_enable": "True", - } + + config = enable_disable_config(True) await ops_test.model.applications[DATABASE_APP_NAME].set_config(config) await ops_test.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active") @@ -90,22 +135,6 @@ async def test_plugins(ops_test: OpsTest) -> None: logger.info("checking that the plugins are enabled") with db_connect(host=address, password=password) as connection: connection.autocommit = True - - # Test citext extension enabled. - connection.cursor().execute(CITEXT_EXTENSION_STATEMENT) - - # Test debversion extension enabled. - connection.cursor().execute(DEBVERSION_EXTENSION_STATEMENT) - - # Test hstore extension enabled. - connection.cursor().execute(HSTORE_EXTENSION_STATEMENT) - - # Test pg_trgm extension enabled. - connection.cursor().execute(PG_TRGM_EXTENSION_STATEMENT) - - # Test PL/Python extension enabled. - connection.cursor().execute(PLPYTHON3U_EXTENSION_STATEMENT) - - # Test unaccent extension enabled. - connection.cursor().execute(UNACCENT_EXTENSION_STATEMENT) + for query in sql_tests.values(): + connection.cursor().execute(query) connection.close() diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 35b7ba5943..9e5c63ab74 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -321,12 +321,81 @@ def test_enable_disable_extensions(self, _): plugin_unaccent_enable: default: false type: boolean - profile: - default: production - type: string plugin_debversion_enable: default: false - type: boolean""" + type: boolean + plugin_bloom_enable: + default: false + type: boolean + plugin_btree_gin_enable: + default: false + type: boolean + plugin_btree_gist_enable: + default: false + type: boolean + plugin_cube_enable: + default: false + type: boolean + plugin_dict_int_enable: + default: false + type: boolean + plugin_dict_xsyn_enable: + default: false + type: boolean + plugin_earthdistance_enable: + default: false + type: boolean + plugin_fuzzystrmatch_enable: + default: false + type: boolean + plugin_intarray_enable: + default: false + type: boolean + plugin_isn_enable: + default: false + type: boolean + plugin_lo_enable: + default: false + type: boolean + plugin_ltree_enable: + default: false + type: boolean + plugin_old_snapshot_enable: + default: false + type: boolean + plugin_pg_freespacemap_enable: + default: false + type: boolean + plugin_pgrowlocks_enable: + default: false + type: boolean + plugin_pgstattuple_enable: + default: false + type: boolean + plugin_pg_visibility_enable: + default: false + type: boolean + plugin_seg_enable: + default: false + type: boolean + plugin_tablefunc_enable: + default: false + type: boolean + plugin_tcn_enable: + default: false + type: boolean + plugin_tsm_system_rows_enable: + default: false + type: boolean + plugin_tsm_system_time_enable: + default: false + type: boolean + plugin_uuid_ossp_enable: + default: false + type: boolean + profile: + default: production + type: string""" harness = Harness(PostgresqlOperatorCharm, config=config) self.addCleanup(harness.cleanup) harness.begin()