ironflow_ops_postgres/schema/
indexes.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 IndexInfo {
15 pub name: String,
17 pub is_unique: bool,
19 pub is_primary: bool,
21 pub definition: String,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct ListIndexesOutput {
28 pub indexes: Vec<IndexInfo>,
30}
31
32pub struct ListIndexes {
46 pool: PgPool,
47 schema: String,
48 table: String,
49}
50
51impl ListIndexes {
52 pub fn new(pool: PgPool, schema: impl Into<String>, table: impl Into<String>) -> Self {
54 Self {
55 pool,
56 schema: schema.into(),
57 table: table.into(),
58 }
59 }
60
61 pub async fn run(&self, _ctx: &OperationContext) -> Result<ListIndexesOutput, OperationError> {
67 let rows = sqlx::query(
68 "SELECT i.relname AS index_name, \
69 ix.indisunique AS is_unique, \
70 ix.indisprimary AS is_primary, \
71 pg_get_indexdef(ix.indexrelid) AS definition \
72 FROM pg_index ix \
73 JOIN pg_class i ON i.oid = ix.indexrelid \
74 JOIN pg_class t ON t.oid = ix.indrelid \
75 JOIN pg_namespace n ON n.oid = t.relnamespace \
76 WHERE n.nspname = $1 AND t.relname = $2 \
77 ORDER BY i.relname",
78 )
79 .bind(&self.schema)
80 .bind(&self.table)
81 .fetch_all(&self.pool)
82 .await
83 .map_err(pg_error)?;
84 let indexes = rows
85 .iter()
86 .map(|r| {
87 Ok(IndexInfo {
88 name: r.try_get::<String, _>("index_name").map_err(pg_error)?,
89 is_unique: r.try_get::<bool, _>("is_unique").map_err(pg_error)?,
90 is_primary: r.try_get::<bool, _>("is_primary").map_err(pg_error)?,
91 definition: r.try_get::<String, _>("definition").map_err(pg_error)?,
92 })
93 })
94 .collect::<Result<Vec<_>, OperationError>>()?;
95 Ok(ListIndexesOutput { indexes })
96 }
97}
98
99#[async_trait]
100impl Operation for ListIndexes {
101 fn kind(&self) -> &str {
102 "postgres"
103 }
104 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
105 to_value(&self.run(ctx).await?)
106 }
107 fn input(&self) -> Option<Value> {
108 Some(serde_json::json!({ "schema": self.schema, "table": self.table }))
109 }
110}
111
112impl TypedOperation for ListIndexes {
113 type Output = ListIndexesOutput;
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[tokio::test]
121 async fn kind() {
122 let pool = PgPool::connect_lazy("postgres://localhost/test").unwrap();
123 let op = ListIndexes::new(pool, "public", "users");
124 assert_eq!(op.kind(), "postgres");
125 }
126}