Skip to content

Commit 8411ee6

Browse files
authored
[DPE-1086] Add new extensions/plugins (#363)
* Add new extensions/plugins * fix plugins ordering * increment lib patch * update rock image hash
1 parent 834e4ce commit 8411ee6

File tree

6 files changed

+181
-5
lines changed

6 files changed

+181
-5
lines changed

config.yaml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,82 @@ options:
219219
default: false
220220
type: boolean
221221
description: Enable spi extension.
222+
plugin_bool_plperl_enable:
223+
default: false
224+
type: boolean
225+
description: Enable bool_plperl extension.
226+
plugin_hll_enable:
227+
default: false
228+
type: boolean
229+
description: Enable hll extension.
230+
plugin_hypopg_enable:
231+
default: false
232+
type: boolean
233+
description: Enable hypopg extension.
234+
plugin_ip4r_enable:
235+
default: false
236+
type: boolean
237+
description: Enable ip4r extension.
238+
plugin_plperl_enable:
239+
default: false
240+
type: boolean
241+
description: Enable plperl extension.
242+
plugin_jsonb_plperl_enable:
243+
default: false
244+
type: boolean
245+
description: Enable jsonb_plperl extension.
246+
plugin_orafce_enable:
247+
default: false
248+
type: boolean
249+
description: Enable orafce extension.
250+
plugin_pg_similarity_enable:
251+
default: false
252+
type: boolean
253+
description: Enable pg_similarity extension.
254+
plugin_prefix_enable:
255+
default: false
256+
type: boolean
257+
description: Enable prefix extension.
258+
plugin_rdkit_enable:
259+
default: false
260+
type: boolean
261+
description: Enable rdkit extension.
262+
plugin_tds_fdw_enable:
263+
default: false
264+
type: boolean
265+
description: Enable tds_fdw extension.
266+
plugin_icu_ext_enable:
267+
default: false
268+
type: boolean
269+
description: Enable icu_ext extension.
270+
plugin_pltcl_enable:
271+
default: false
272+
type: boolean
273+
description: Enable pltcl extension.
274+
plugin_postgis_enable:
275+
default: false
276+
type: boolean
277+
description: Enable postgis extension.
278+
plugin_address_standardizer_enable:
279+
default: false
280+
type: boolean
281+
description: Enable address_standardizer extension.
282+
plugin_postgis_raster_enable:
283+
default: false
284+
type: boolean
285+
description: Enable postgis_raster extension.
286+
plugin_address_standardizer_data_us_enable:
287+
default: false
288+
type: boolean
289+
description: Enable address_standardizer_data_us extension.
290+
plugin_postgis_tiger_geocoder_enable:
291+
default: false
292+
type: boolean
293+
description: Enable postgis_tiger_geocoder extension.
294+
plugin_postgis_topology_enable:
295+
default: false
296+
type: boolean
297+
description: Enable postgis_topology extension.
222298
plugin_vector_enable:
223299
default: false
224300
type: boolean

lib/charms/postgresql_k8s/v0/postgresql.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
Any charm using this library should import the `psycopg2` or `psycopg2-binary` dependency.
2020
"""
2121
import logging
22+
from collections import OrderedDict
2223
from typing import Dict, List, Optional, Set, Tuple
2324

2425
import psycopg2
@@ -34,10 +35,21 @@
3435

3536
# Increment this PATCH version before using `charmcraft publish-lib` or reset
3637
# to 0 if you are raising the major API version
37-
LIBPATCH = 21
38+
LIBPATCH = 22
3839

3940
INVALID_EXTRA_USER_ROLE_BLOCKING_MESSAGE = "invalid role(s) for extra user roles"
4041

42+
REQUIRED_PLUGINS = {
43+
"address_standardizer": ["postgis"],
44+
"address_standardizer_data_us": ["postgis"],
45+
"jsonb_plperl": ["plperl"],
46+
"postgis_raster": ["postgis"],
47+
"postgis_tiger_geocoder": ["postgis", "fuzzystrmatch"],
48+
"postgis_topology": ["postgis"],
49+
}
50+
DEPENDENCY_PLUGINS = set()
51+
for dependencies in REQUIRED_PLUGINS.values():
52+
DEPENDENCY_PLUGINS |= set(dependencies)
4153

4254
logger = logging.getLogger(__name__)
4355

@@ -289,12 +301,18 @@ def enable_disable_extensions(self, extensions: Dict[str, bool], database: str =
289301
cursor.execute("SELECT datname FROM pg_database WHERE NOT datistemplate;")
290302
databases = {database[0] for database in cursor.fetchall()}
291303

304+
ordered_extensions = OrderedDict()
305+
for plugin in DEPENDENCY_PLUGINS:
306+
ordered_extensions[plugin] = extensions.get(plugin, False)
307+
for extension, enable in extensions.items():
308+
ordered_extensions[extension] = enable
309+
292310
# Enable/disabled the extension in each database.
293311
for database in databases:
294312
with self._connect_to_database(
295313
database=database
296314
) as connection, connection.cursor() as cursor:
297-
for extension, enable in extensions.items():
315+
for extension, enable in ordered_extensions.items():
298316
cursor.execute(
299317
f"CREATE EXTENSION IF NOT EXISTS {extension};"
300318
if enable

metadata.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ resources:
2828
postgresql-image:
2929
type: oci-image
3030
description: OCI image for PostgreSQL
31-
upstream-source: ghcr.io/canonical/charmed-postgresql@sha256:899f5455fa4557b7060990880fb0ae1fa3b21c0ab2e72ad863cc16d0b3f25fee
31+
upstream-source: ghcr.io/canonical/charmed-postgresql@sha256:ff74e3e8fc0e08e7b952cb69477f1a39e94e7caa156f06bbb844e752bec42f0b
3232

3333
peers:
3434
database-peers:

src/charm.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from charms.loki_k8s.v0.loki_push_api import LogProxyConsumer
1616
from charms.observability_libs.v1.kubernetes_service_patch import KubernetesServicePatch
1717
from charms.postgresql_k8s.v0.postgresql import (
18+
REQUIRED_PLUGINS,
1819
PostgreSQL,
1920
PostgreSQLEnableDisableExtensionError,
2021
PostgreSQLUpdateUserPasswordError,
@@ -83,6 +84,8 @@
8384

8485
logger = logging.getLogger(__name__)
8586

87+
EXTENSIONS_DEPENDENCY_MESSAGE = "Unsatisfied plugin dependencies. Please check the logs"
88+
8689
# http{x,core} clutter the logs with debug messages
8790
logging.getLogger("httpcore").setLevel(logging.ERROR)
8891
logging.getLogger("httpx").setLevel(logging.ERROR)
@@ -459,10 +462,10 @@ def enable_disable_extensions(self, database: str = None) -> None:
459462
database: optional database where to enable/disable the extension.
460463
"""
461464
spi_module = ["refint", "autoinc", "insert_username", "moddatetime"]
465+
plugins_exception = {"uuid_ossp": '"uuid-ossp"'}
462466
original_status = self.unit.status
463467
extensions = {}
464468
# collect extensions
465-
plugins_exception = {"uuid_ossp": '"uuid-ossp"'}
466469
for plugin in self.config.plugin_keys():
467470
enable = self.config[plugin]
468471

@@ -473,7 +476,12 @@ def enable_disable_extensions(self, database: str = None) -> None:
473476
extensions[ext] = enable
474477
continue
475478
extension = plugins_exception.get(extension, extension)
479+
if self._check_extension_dependencies(extension, enable):
480+
self.unit.status = BlockedStatus(EXTENSIONS_DEPENDENCY_MESSAGE)
481+
return
476482
extensions[extension] = enable
483+
if self.is_blocked and self.unit.status.message == EXTENSIONS_DEPENDENCY_MESSAGE:
484+
self.unit.status = ActiveStatus()
477485
if not isinstance(original_status, UnknownStatus):
478486
self.unit.status = WaitingStatus("Updating extensions")
479487
try:
@@ -483,6 +491,19 @@ def enable_disable_extensions(self, database: str = None) -> None:
483491
if not isinstance(original_status, UnknownStatus):
484492
self.unit.status = original_status
485493

494+
def _check_extension_dependencies(self, extension: str, enable: bool) -> bool:
495+
skip = False
496+
if enable and extension in REQUIRED_PLUGINS:
497+
for ext in REQUIRED_PLUGINS[extension]:
498+
if not self.config[f"plugin_{ext}_enable"]:
499+
skip = True
500+
logger.exception(
501+
"cannot enable %s, extension required %s to be enabled before",
502+
extension,
503+
ext,
504+
)
505+
return skip
506+
486507
def _add_members(self, event) -> None:
487508
"""Add new cluster members.
488509

src/config.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,27 @@ class CharmConfig(BaseConfigModel):
6262
plugin_tsm_system_rows_enable: bool
6363
plugin_tsm_system_time_enable: bool
6464
plugin_uuid_ossp_enable: bool
65-
plugin_vector_enable: bool
6665
plugin_spi_enable: bool
66+
plugin_bool_plperl_enable: bool
67+
plugin_hll_enable: bool
68+
plugin_hypopg_enable: bool
69+
plugin_ip4r_enable: bool
70+
plugin_plperl_enable: bool
71+
plugin_jsonb_plperl_enable: bool
72+
plugin_orafce_enable: bool
73+
plugin_pg_similarity_enable: bool
74+
plugin_prefix_enable: bool
75+
plugin_rdkit_enable: bool
76+
plugin_tds_fdw_enable: bool
77+
plugin_icu_ext_enable: bool
78+
plugin_pltcl_enable: bool
79+
plugin_postgis_enable: bool
80+
plugin_address_standardizer_enable: bool
81+
plugin_address_standardizer_data_us_enable: bool
82+
plugin_postgis_tiger_geocoder_enable: bool
83+
plugin_postgis_topology_enable: bool
84+
plugin_postgis_raster_enable: bool
85+
plugin_vector_enable: bool
6786
request_date_style: Optional[str]
6887
request_standard_conforming_strings: Optional[bool]
6988
request_time_zone: Optional[str]

tests/integration/test_plugins.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,29 @@
5757
AUTOINC_EXTENSION_STATEMENT = "CREATE TABLE ids (id int4, idesc text);CREATE TRIGGER ids_nextid BEFORE INSERT OR UPDATE ON ids FOR EACH ROW EXECUTE PROCEDURE autoinc (id, next_id);"
5858
INSERT_USERNAME_EXTENSION_STATEMENT = "CREATE TABLE username_test (name text, username text not null);CREATE TRIGGER insert_usernames BEFORE INSERT OR UPDATE ON username_test FOR EACH ROW EXECUTE PROCEDURE insert_username (username);"
5959
MODDATETIME_EXTENSION_STATEMENT = "CREATE TABLE mdt (moddate timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL);CREATE TRIGGER mdt_moddatetime BEFORE UPDATE ON mdt FOR EACH ROW EXECUTE PROCEDURE moddatetime (moddate);"
60+
BOOL_PLPERL_EXTENSION_STATEMENT = "CREATE FUNCTION hello_bool(bool) RETURNS TEXT TRANSFORM FOR TYPE bool LANGUAGE plperl AS $$ my $with_world = shift; return sprintf('hello%s', $with_world ? ' world' : ''); $$;"
61+
HLL_EXTENSION_STATEMENT = "CREATE TABLE hll_test (users hll);"
62+
HYPOPG_EXTENSION_STATEMENT = "CREATE TABLE hypopg_test (id integer, val text); SELECT hypopg_create_index('CREATE INDEX ON hypopg_test (id)');"
63+
IP4R_EXTENSION_STATEMENT = "CREATE TABLE ip4r_test (ip ip4);"
64+
JSONB_PLPERL_EXTENSION_STATEMENT = "CREATE OR REPLACE FUNCTION jsonb_plperl_test(val jsonb) RETURNS jsonb TRANSFORM FOR TYPE jsonb LANGUAGE plperl as $$ return $_[0]; $$;"
65+
ORAFCE_EXTENSION_STATEMENT = "SELECT add_months(date '2005-05-31',1);"
66+
PG_SIMILARITY_EXTENSION_STATEMENT = "SHOW pg_similarity.levenshtein_threshold;"
67+
PLPERL_EXTENSION_STATEMENT = "CREATE OR REPLACE FUNCTION plperl_test(name text) RETURNS text AS $$ return $_SHARED{$_[0]}; $$ LANGUAGE plperl;"
68+
PREFIX_EXTENSION_STATEMENT = "SELECT '123'::prefix_range @> '123456';"
69+
RDKIT_EXTENSION_STATEMENT = "SELECT is_valid_smiles('CCC');"
70+
TDS_FDW_EXTENSION_STATEMENT = "CREATE SERVER mssql_svr FOREIGN DATA WRAPPER tds_fdw OPTIONS (servername 'tds_fdw_test', port '3306', database 'tds_fdw_test', tds_version '7.1');"
71+
ICU_EXT_EXTENSION_STATEMENT = (
72+
'CREATE COLLATION "vat-lat" (provider = icu, locale = "la-VA-u-kn-true")'
73+
)
74+
PLTCL_EXTENSION_STATEMENT = (
75+
"CREATE FUNCTION pltcl_test(integer) RETURNS integer AS $$ return $1 $$ LANGUAGE pltcl STRICT;"
76+
)
77+
POSTGIS_EXTENSION_STATEMENT = "SELECT PostGIS_Full_Version();"
78+
ADDRESS_STANDARDIZER_EXTENSION_STATEMENT = "SELECT num, street, city, zip, zipplus FROM parse_address('1 Devonshire Place, Boston, MA 02109-1234');"
79+
ADDRESS_STANDARDIZER_DATA_US_EXTENSION_STATEMENT = "SELECT house_num, name, suftype, city, country, state, unit FROM standardize_address('us_lex', 'us_gaz', 'us_rules', 'One Devonshire Place, PH 301, Boston, MA 02109');"
80+
POSTGIS_TIGER_GEOCODER_EXTENSION_STATEMENT = "SELECT * FROM standardize_address('tiger.pagc_lex', 'tiger.pagc_gaz', 'tiger.pagc_rules', 'One Devonshire Place, PH 301, Boston, MA 02109-1234');"
81+
POSTGIS_TOPOLOGY_STATEMENT = "SELECT topology.CreateTopology('nyc_topo', 26918, 0.5);"
82+
POSTGIS_RASTER_STATEMENT = "CREATE TABLE test_postgis_raster (name varchar, rast raster);"
6083
VECTOR_EXTENSION_STATEMENT = (
6184
"CREATE TABLE vector_test (id bigserial PRIMARY KEY, embedding vector(3));"
6285
)
@@ -106,6 +129,25 @@ async def test_plugins(ops_test: OpsTest) -> None:
106129
INSERT_USERNAME_EXTENSION_STATEMENT,
107130
MODDATETIME_EXTENSION_STATEMENT,
108131
],
132+
"plugin_bool_plperl_enable": BOOL_PLPERL_EXTENSION_STATEMENT,
133+
"plugin_hll_enable": HLL_EXTENSION_STATEMENT,
134+
"plugin_postgis_enable": POSTGIS_EXTENSION_STATEMENT,
135+
"plugin_hypopg_enable": HYPOPG_EXTENSION_STATEMENT,
136+
"plugin_ip4r_enable": IP4R_EXTENSION_STATEMENT,
137+
"plugin_plperl_enable": PLPERL_EXTENSION_STATEMENT,
138+
"plugin_jsonb_plperl_enable": JSONB_PLPERL_EXTENSION_STATEMENT,
139+
"plugin_orafce_enable": ORAFCE_EXTENSION_STATEMENT,
140+
"plugin_pg_similarity_enable": ORAFCE_EXTENSION_STATEMENT,
141+
"plugin_prefix_enable": PREFIX_EXTENSION_STATEMENT,
142+
"plugin_rdkit_enable": RDKIT_EXTENSION_STATEMENT,
143+
"plugin_tds_fdw_enable": TDS_FDW_EXTENSION_STATEMENT,
144+
"plugin_icu_ext_enable": ICU_EXT_EXTENSION_STATEMENT,
145+
"plugin_pltcl_enable": PLTCL_EXTENSION_STATEMENT,
146+
"plugin_address_standardizer_enable": ADDRESS_STANDARDIZER_EXTENSION_STATEMENT,
147+
"plugin_address_standardizer_data_us_enable": ADDRESS_STANDARDIZER_DATA_US_EXTENSION_STATEMENT,
148+
"plugin_postgis_tiger_geocoder_enable": POSTGIS_TIGER_GEOCODER_EXTENSION_STATEMENT,
149+
"plugin_postgis_raster_enable": POSTGIS_RASTER_STATEMENT,
150+
"plugin_postgis_topology_enable": POSTGIS_TOPOLOGY_STATEMENT,
109151
"plugin_vector_enable": VECTOR_EXTENSION_STATEMENT,
110152
}
111153

0 commit comments

Comments
 (0)