drizzle_cli/commands/
push.rs1use crate::config::{Casing, DrizzleConfig};
8use crate::error::CliError;
9use crate::output;
10use crate::snapshot::parse_result_to_snapshot;
11
12#[derive(Debug, Clone)]
13pub struct PushOptions {
14 pub cli_verbose: bool,
15 pub cli_strict: bool,
16 pub force: bool,
17 pub cli_explain: bool,
18 pub casing: Option<Casing>,
19 pub extensions_filters: Option<Vec<String>>,
20}
21
22pub fn run(
24 config: &DrizzleConfig,
25 db_name: Option<&str>,
26 opts: PushOptions,
27) -> Result<(), CliError> {
28 use drizzle_migrations::parser::SchemaParser;
29
30 let db = config.database(db_name)?;
31
32 let verbose = opts.cli_verbose || db.verbose;
34 let explain = opts.cli_explain;
35 let _effective_casing = opts.casing.unwrap_or_else(|| db.effective_casing());
36 let _extensions_filters = opts.extensions_filters;
39
40 if opts.cli_strict {
41 println!(
42 "{}",
43 output::warning("Deprecated: Do not use '--strict'. Use '--explain' instead.")
44 );
45 return Err(CliError::Other("strict flag is deprecated".into()));
46 }
47
48 if !config.is_single_database() {
49 let name = db_name.unwrap_or("(default)");
50 println!("{}: {}", output::label("Database"), name);
51 }
52
53 println!("{}", output::heading("Pushing schema to database..."));
54 println!();
55
56 let credentials = db.credentials()?;
58 let credentials = match credentials {
59 Some(c) => c,
60 None => {
61 println!("{}", output::warning("No database credentials configured."));
62 println!();
63 println!("Add credentials to your drizzle.config.toml:");
64 println!();
65 println!(" {}", output::muted("[dbCredentials]"));
66 match db.dialect.to_base() {
67 drizzle_types::Dialect::SQLite => {
68 println!(" {}", output::muted("url = \"./dev.db\""));
69 }
70 drizzle_types::Dialect::PostgreSQL => {
71 println!(
72 " {}",
73 output::muted("url = \"postgres://user:pass@localhost:5432/db\"")
74 );
75 }
76 drizzle_types::Dialect::MySQL => {
77 println!(
80 " {}",
81 output::muted("url = \"mysql://user:pass@localhost:3306/db\"")
82 );
83 }
84 }
85 println!();
86 println!("Or use an environment variable:");
87 println!();
88 println!(" {}", output::muted("[dbCredentials]"));
89 println!(" {}", output::muted("url = { env = \"DATABASE_URL\" }"));
90 return Ok(());
91 }
92 };
93
94 let schema_files = db.schema_files()?;
96 if schema_files.is_empty() {
97 return Err(CliError::NoSchemaFiles(db.schema_display()));
98 }
99
100 println!(
101 " {} {} schema file(s)",
102 output::label("Parsing"),
103 schema_files.len()
104 );
105
106 let mut combined_code = String::new();
107 for path in &schema_files {
108 let code = std::fs::read_to_string(path)
109 .map_err(|e| CliError::IoError(format!("Failed to read {}: {}", path.display(), e)))?;
110 combined_code.push_str(&code);
111 combined_code.push('\n');
112 }
113
114 let parse_result = SchemaParser::parse(&combined_code);
115
116 if parse_result.tables.is_empty() && parse_result.indexes.is_empty() {
117 println!(
118 "{}",
119 output::warning("No tables or indexes found in schema files.")
120 );
121 return Ok(());
122 }
123
124 println!(
125 " {} {} table(s), {} index(es)",
126 output::label("Found"),
127 parse_result.tables.len(),
128 parse_result.indexes.len()
129 );
130
131 let dialect = db.dialect.to_base();
133 let desired_snapshot = parse_result_to_snapshot(&parse_result, dialect);
134
135 let plan = crate::db::plan_push(&credentials, db.dialect, &desired_snapshot, db.breakpoints)?;
137
138 if !plan.warnings.is_empty() {
139 println!("{}", output::warning("Warnings:"));
140 for w in &plan.warnings {
141 println!(" {} {}", output::warning("-"), w);
142 }
143 println!();
144 }
145
146 if explain || verbose {
148 if plan.sql_statements.is_empty() {
149 println!("{}", output::success("No schema changes detected."));
150 return Ok(());
151 }
152
153 println!("{}", output::muted("--- Planned SQL ---"));
154 println!();
155 for stmt in &plan.sql_statements {
156 println!("{stmt}\n");
157 }
158 println!("{}", output::muted("--- End SQL ---"));
159 println!();
160 }
161
162 if explain {
164 return Ok(());
165 }
166
167 if plan.sql_statements.is_empty() {
168 println!("{}", output::success("No schema changes detected."));
169 return Ok(());
170 }
171
172 crate::db::apply_push(&credentials, db.dialect, &plan, opts.force)?;
174
175 println!("{}", output::success("Push complete!"));
176
177 Ok(())
178}