database_replicator/postgres/
extensions.rs1use anyhow::{Context, Result};
5use tokio_postgres::Client;
6
7#[derive(Debug, Clone)]
8pub struct Extension {
9 pub name: String,
10 pub version: String,
11}
12
13#[derive(Debug, Clone)]
14pub struct AvailableExtension {
15 pub name: String,
16 pub default_version: Option<String>,
17 pub installed_version: Option<String>,
18}
19
20pub async fn get_installed_extensions(client: &Client) -> Result<Vec<Extension>> {
22 let rows = client
23 .query(
24 "SELECT extname, extversion FROM pg_extension WHERE extname != 'plpgsql' ORDER BY extname",
25 &[],
26 )
27 .await
28 .context("Failed to query installed extensions")?;
29
30 let extensions = rows
31 .iter()
32 .map(|row| Extension {
33 name: row.get(0),
34 version: row.get(1),
35 })
36 .collect();
37
38 Ok(extensions)
39}
40
41pub async fn get_available_extensions(client: &Client) -> Result<Vec<AvailableExtension>> {
43 let rows = client
44 .query(
45 "SELECT name, default_version, installed_version FROM pg_available_extensions ORDER BY name",
46 &[],
47 )
48 .await
49 .context("Failed to query available extensions")?;
50
51 let extensions = rows
52 .iter()
53 .map(|row| AvailableExtension {
54 name: row.get(0),
55 default_version: row.get(1),
56 installed_version: row.get(2),
57 })
58 .collect();
59
60 Ok(extensions)
61}
62
63pub async fn get_preloaded_libraries(client: &Client) -> Result<Vec<String>> {
65 let row = client
66 .query_one("SHOW shared_preload_libraries", &[])
67 .await
68 .context("Failed to query shared_preload_libraries")?;
69
70 let libs_str: String = row.get(0);
71
72 let libraries = libs_str
74 .split(',')
75 .map(|s| s.trim().to_string())
76 .filter(|s| !s.is_empty())
77 .collect();
78
79 Ok(libraries)
80}
81
82const PRELOAD_REQUIRED_EXTENSIONS: &[&str] = &[
84 "timescaledb",
85 "citus",
86 "pg_stat_statements",
87 "pg_cron",
88 "auto_explain",
89 "pg_partman_bgw",
90];
91
92pub fn requires_preload(extension_name: &str) -> bool {
94 PRELOAD_REQUIRED_EXTENSIONS.contains(&extension_name)
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_requires_preload() {
103 assert!(requires_preload("timescaledb"));
104 assert!(requires_preload("citus"));
105 assert!(requires_preload("pg_stat_statements"));
106 assert!(!requires_preload("pg_trgm"));
107 assert!(!requires_preload("uuid-ossp"));
108 }
109}