systemprompt_cli/commands/infrastructure/db/
mod.rs1mod admin;
14mod admin_migrate;
15mod admin_migrate_down;
16mod admin_migrate_mark_applied;
17mod admin_migrate_plan;
18mod admin_migrate_repair;
19mod admin_migrate_status;
20mod admin_migrations;
21mod commands;
22mod dispatch;
23mod doctor;
24pub mod helpers;
25mod introspect;
26mod query;
27mod schema;
28mod types;
29
30use anyhow::{Context, Result, bail};
31use std::sync::Arc;
32use systemprompt_database::{DatabaseAdminService, DbPool, QueryExecutor};
33
34use crate::cli_settings::CliConfig;
35use crate::context::CommandContext;
36use crate::interactive::require_confirmation;
37use crate::shared::render_result;
38use dispatch::{dispatch_profile_migration, dispatch_standalone_migration};
39
40pub use commands::{DbCommands, MigrationsCommands};
41pub use types::*;
42
43pub(crate) use helpers::format_bytes;
44
45pub async fn execute(cmd: DbCommands, ctx: &CommandContext) -> Result<()> {
46 let config = &ctx.cli;
47 let Some(cmd) = (match ctx.database_context() {
48 Some(db_ctx) => dispatch_standalone_migration(cmd, db_ctx, config).await?,
49 None => dispatch_profile_migration(cmd, config).await?,
50 }) else {
51 return Ok(());
52 };
53
54 let (pool, admin_service, query_executor) = connect_services(ctx).await?;
55
56 match cmd {
57 DbCommands::Query { sql, limit, offset } => {
58 let params = query::QueryParams::new(&sql, limit, offset);
59 run_query(&query_executor, &admin_service, ¶ms, config).await
60 },
61 DbCommands::Execute { sql, yes } => {
62 require_confirmation(
63 ctx.prompter(),
64 &format!("Execute this write against the database?\n {sql}"),
65 yes,
66 config,
67 )?;
68 run_write(&query_executor, &sql, config).await
69 },
70 DbCommands::Tables { filter, exact } => {
71 schema::execute_tables(&admin_service, filter, exact, config).await
72 },
73 DbCommands::Describe { table_name } => {
74 schema::execute_describe(&admin_service, &table_name, config).await
75 },
76 DbCommands::Info => schema::execute_info(&admin_service, config).await,
77 DbCommands::Migrate { .. }
78 | DbCommands::MigrateDown { .. }
79 | DbCommands::MigrateRepair { .. }
80 | DbCommands::MigrateMarkApplied { .. } => {
81 bail!("migration command was not consumed by the migration dispatcher")
82 },
83 DbCommands::Migrations { cmd } => {
84 admin::execute_migrations(ctx.app_context().await?, cmd, config).await
85 },
86 DbCommands::MigratePlan { extension, json } => {
87 admin::execute_migrate_plan(ctx.app_context().await?, extension.as_ref(), json, config)
88 .await
89 },
90 DbCommands::MigrateStatus { extension, json } => {
91 admin::execute_migrate_status(
92 ctx.app_context().await?,
93 extension.as_ref(),
94 json,
95 config,
96 )
97 .await
98 },
99 DbCommands::AssignAdmin { user } => {
100 if ctx.is_database_scoped() {
101 bail!("assign-admin requires full profile context");
102 }
103 admin::execute_assign_admin(ctx.app_context().await?, &user, config).await
104 },
105 DbCommands::Status => admin::execute_status(&admin_service, config).await,
106 DbCommands::Count { table_name } => {
107 schema::execute_count(&admin_service, &table_name, config).await
108 },
109 DbCommands::Indexes { table } => {
110 introspect::execute_indexes(&admin_service, table, config).await
111 },
112 DbCommands::Size => introspect::execute_size(&admin_service, config).await,
113 DbCommands::Doctor => doctor::execute_doctor(&pool, config).await,
114 }
115}
116
117async fn connect_services(
118 ctx: &CommandContext,
119) -> Result<(DbPool, DatabaseAdminService, QueryExecutor)> {
120 let pool = ctx
121 .db_pool()
122 .await
123 .context("Failed to connect to database. Check your profile configuration.")?;
124 let write_pool = pool.write_pool();
125 let admin_service = DatabaseAdminService::new(Arc::clone(&write_pool));
126 let query_executor = QueryExecutor::new(write_pool);
127 Ok((pool, admin_service, query_executor))
128}
129
130async fn run_query(
131 executor: &QueryExecutor,
132 admin_service: &DatabaseAdminService,
133 params: &query::QueryParams<'_>,
134 config: &CliConfig,
135) -> Result<()> {
136 let result = query::execute_query(executor, admin_service, params, config).await?;
137 render_result(&result, config);
138 Ok(())
139}
140
141async fn run_write(executor: &QueryExecutor, sql: &str, config: &CliConfig) -> Result<()> {
142 let result = query::execute_write(executor, sql, config).await?;
143 render_result(&result, config);
144 Ok(())
145}