ironflow_ops_postgres/admin/
size.rs1use async_trait::async_trait;
4use ironflow_core::error::OperationError;
5use ironflow_core::operation::{Operation, OperationContext, TypedOperation};
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use sqlx::{PgPool, Row};
9
10use crate::helpers::{pg_error, to_value};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct DatabaseSizeOutput {
15 pub database: String,
17 pub size_bytes: i64,
19}
20
21pub struct DatabaseSize {
35 pool: PgPool,
36 database: String,
37}
38
39impl DatabaseSize {
40 pub fn new(pool: PgPool, database: impl Into<String>) -> Self {
42 Self {
43 pool,
44 database: database.into(),
45 }
46 }
47
48 pub async fn run(&self, _ctx: &OperationContext) -> Result<DatabaseSizeOutput, OperationError> {
54 let row = sqlx::query("SELECT pg_database_size($1) AS size")
55 .bind(&self.database)
56 .fetch_one(&self.pool)
57 .await
58 .map_err(pg_error)?;
59 let size: i64 = row.try_get("size").map_err(pg_error)?;
60 Ok(DatabaseSizeOutput {
61 database: self.database.clone(),
62 size_bytes: size,
63 })
64 }
65}
66
67#[async_trait]
68impl Operation for DatabaseSize {
69 fn kind(&self) -> &str {
70 "postgres"
71 }
72 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
73 to_value(&self.run(ctx).await?)
74 }
75 fn input(&self) -> Option<Value> {
76 Some(serde_json::json!({ "database": self.database }))
77 }
78}
79
80impl TypedOperation for DatabaseSize {
81 type Output = DatabaseSizeOutput;
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct TableSizeOutput {
87 pub table: String,
89 pub total_bytes: i64,
91 pub table_bytes: i64,
93 pub index_bytes: i64,
95}
96
97pub struct TableSize {
111 pool: PgPool,
112 schema: String,
113 table: String,
114}
115
116impl TableSize {
117 pub fn new(pool: PgPool, schema: impl Into<String>, table: impl Into<String>) -> Self {
119 Self {
120 pool,
121 schema: schema.into(),
122 table: table.into(),
123 }
124 }
125
126 pub async fn run(&self, _ctx: &OperationContext) -> Result<TableSizeOutput, OperationError> {
132 let qualified = format!("{}.{}", self.schema, self.table);
133 let row = sqlx::query(
134 "SELECT pg_total_relation_size($1::regclass) AS total, \
135 pg_table_size($1::regclass) AS tbl, \
136 pg_indexes_size($1::regclass) AS idx",
137 )
138 .bind(&qualified)
139 .fetch_one(&self.pool)
140 .await
141 .map_err(pg_error)?;
142 Ok(TableSizeOutput {
143 table: qualified,
144 total_bytes: row.try_get("total").map_err(pg_error)?,
145 table_bytes: row.try_get("tbl").map_err(pg_error)?,
146 index_bytes: row.try_get("idx").map_err(pg_error)?,
147 })
148 }
149}
150
151#[async_trait]
152impl Operation for TableSize {
153 fn kind(&self) -> &str {
154 "postgres"
155 }
156 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
157 to_value(&self.run(ctx).await?)
158 }
159 fn input(&self) -> Option<Value> {
160 Some(serde_json::json!({ "schema": self.schema, "table": self.table }))
161 }
162}
163
164impl TypedOperation for TableSize {
165 type Output = TableSizeOutput;
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[tokio::test]
173 async fn database_size_kind() {
174 let pool = PgPool::connect_lazy("postgres://localhost/test").unwrap();
175 let op = DatabaseSize::new(pool, "test");
176 assert_eq!(op.kind(), "postgres");
177 }
178
179 #[tokio::test]
180 async fn table_size_kind() {
181 let pool = PgPool::connect_lazy("postgres://localhost/test").unwrap();
182 let op = TableSize::new(pool, "public", "users");
183 assert_eq!(op.kind(), "postgres");
184 }
185}