use dbmcp_server::pagination::Pager;
use dbmcp_server::types::ListEntriesResponse;
use super::prelude::*;
use crate::types::ListTriggersRequest;
const NAME: &str = "listTriggers";
const TITLE: &str = "List Triggers";
const DESCRIPTION: &str = include_str!("../../assets/tools/list_triggers.md");
pub(crate) struct ListTriggersTool;
impl ToolBase for ListTriggersTool {
type Parameter = ListTriggersRequest;
type Output = ListEntriesResponse;
type Error = ErrorData;
fn name() -> Cow<'static, str> {
NAME.into()
}
fn title() -> Option<String> {
Some(TITLE.into())
}
fn description() -> Option<Cow<'static, str>> {
Some(DESCRIPTION.into())
}
fn annotations() -> Option<ToolAnnotations> {
Some(
ToolAnnotations::new()
.read_only(true)
.destructive(false)
.idempotent(true)
.open_world(false),
)
}
fn input_schema() -> Option<Arc<JsonObject>> {
Some(input_schema::<Self::Parameter>(true))
}
fn output_schema() -> Option<Arc<JsonObject>> {
Some(output_schema::<Self::Output>())
}
}
impl AsyncTool<SqliteHandler> for ListTriggersTool {
async fn invoke(handler: &SqliteHandler, params: Self::Parameter) -> Result<Self::Output, Self::Error> {
handler.list_triggers(params).await
}
}
const BRIEF_SQL: &str = r"
SELECT name
FROM sqlite_schema
WHERE type = 'trigger'
AND name NOT LIKE 'sqlite_%'
AND (?1 IS NULL OR name LIKE '%' || ?1 || '%')
ORDER BY name
LIMIT ?2 OFFSET ?3";
const DETAILED_SQL: &str = r"
SELECT
name,
json_object(
'schema', 'main',
'table', tbl_name,
'definition', sql
) AS entry
FROM sqlite_schema
WHERE type = 'trigger'
AND name NOT LIKE 'sqlite_%'
AND sql IS NOT NULL
AND (?1 IS NULL OR name LIKE '%' || ?1 || '%')
ORDER BY name
LIMIT ?2 OFFSET ?3";
impl SqliteHandler {
pub async fn list_triggers(
&self,
ListTriggersRequest {
cursor,
search,
detailed,
}: ListTriggersRequest,
) -> Result<ListEntriesResponse, ErrorData> {
let pattern = search.as_deref().map(str::trim).filter(|s| !s.is_empty());
let pager = Pager::new(cursor, self.config.page_size);
if detailed {
let rows: Vec<(String, sqlx::types::Json<serde_json::Value>)> = self
.connection
.fetch(
sqlx::query(DETAILED_SQL)
.bind(pattern)
.bind(pager.limit())
.bind(pager.offset()),
None,
)
.await?;
let (rows, next_cursor) = pager.paginate(rows);
return Ok(ListEntriesResponse::detailed(
rows.into_iter().map(|(name, json)| (name, json.0)).collect(),
next_cursor,
));
}
let rows: Vec<String> = self
.connection
.fetch_scalar(
sqlx::query(BRIEF_SQL)
.bind(pattern)
.bind(pager.limit())
.bind(pager.offset()),
None,
)
.await?;
let (triggers, next_cursor) = pager.paginate(rows);
Ok(ListEntriesResponse::brief(triggers, next_cursor))
}
}