mz_deploy/client/
provisioning.rs1use crate::client::auto_scaling::strategy_to_cluster_option;
31use crate::client::connection::ProvisioningClient;
32use crate::client::errors::ConnectionError;
33use crate::client::models::{ClusterConfig, ClusterOptions};
34use crate::client::quote_identifier;
35use mz_sql_parser::ast::display::AstDisplay;
36
37impl ProvisioningClient<'_> {
38 pub async fn create_database(&self, database: &str) -> Result<(), ConnectionError> {
40 let sql = format!(
41 "CREATE DATABASE IF NOT EXISTS {}",
42 quote_identifier(database)
43 );
44
45 self.client.execute(&sql, &[]).await.map_err(|e| {
46 ConnectionError::DatabaseCreationFailed {
47 database: database.to_string(),
48 source: Box::new(e),
49 }
50 })?;
51
52 Ok(())
53 }
54
55 pub async fn create_schema(&self, database: &str, schema: &str) -> Result<(), ConnectionError> {
57 let sql = format!(
58 "CREATE SCHEMA IF NOT EXISTS {}.{}",
59 quote_identifier(database),
60 quote_identifier(schema)
61 );
62
63 self.client.execute(&sql, &[]).await.map_err(|e| {
64 ConnectionError::SchemaCreationFailed {
65 database: database.to_string(),
66 schema: schema.to_string(),
67 source: Box::new(e),
68 }
69 })?;
70
71 Ok(())
72 }
73
74 pub async fn create_cluster(
77 &self,
78 name: &str,
79 options: &ClusterOptions,
80 ) -> Result<(), ConnectionError> {
81 let mut sql = format!(
82 "CREATE CLUSTER {} (SIZE = '{}', REPLICATION FACTOR = {}",
83 quote_identifier(name),
84 options.size,
85 options.replication_factor
86 );
87 if let Some(strategy) = &options.auto_scaling_strategy {
88 sql.push_str(", ");
89 sql.push_str(&strategy_to_cluster_option(strategy).to_ast_string_simple());
90 }
91 sql.push(')');
92
93 self.client.execute(&sql, &[]).await.map_err(|e| {
94 if e.to_string().contains("already exists") {
95 ConnectionError::ClusterAlreadyExists {
96 name: name.to_string(),
97 }
98 } else {
99 ConnectionError::ClusterCreationFailed {
100 name: name.to_string(),
101 source: Box::new(e),
102 }
103 }
104 })?;
105
106 Ok(())
107 }
108
109 pub async fn create_cluster_with_config(
111 &self,
112 name: &str,
113 config: &ClusterConfig,
114 ) -> Result<(), ConnectionError> {
115 let grants = match config {
116 ClusterConfig::Managed { options, grants } => {
117 self.create_cluster(name, options).await?;
118 grants
119 }
120 ClusterConfig::Unmanaged { replicas, grants } => {
121 let create_cluster_sql =
122 format!("CREATE CLUSTER {} REPLICAS ()", quote_identifier(name));
123
124 self.client
125 .execute(&create_cluster_sql, &[])
126 .await
127 .map_err(|e| {
128 if e.to_string().contains("already exists") {
129 ConnectionError::ClusterAlreadyExists {
130 name: name.to_string(),
131 }
132 } else {
133 ConnectionError::ClusterCreationFailed {
134 name: name.to_string(),
135 source: Box::new(e),
136 }
137 }
138 })?;
139
140 for replica in replicas {
141 let mut options_parts = vec![format!("SIZE = '{}'", replica.size)];
142
143 if let Some(ref az) = replica.availability_zone {
144 options_parts.push(format!("AVAILABILITY ZONE '{}'", az));
145 }
146
147 let create_replica_sql = format!(
148 "CREATE CLUSTER REPLICA {}.{} ({})",
149 quote_identifier(name),
150 quote_identifier(&replica.name),
151 options_parts.join(", ")
152 );
153
154 self.client
155 .execute(&create_replica_sql, &[])
156 .await
157 .map_err(|e| ConnectionError::ClusterCreationFailed {
158 name: format!("{}.{}", name, replica.name),
159 source: Box::new(e),
160 })?;
161 }
162
163 grants
164 }
165 };
166
167 for grant in grants {
168 let sql = format!(
169 "GRANT {} ON CLUSTER {} TO {}",
170 grant.privilege_type,
171 quote_identifier(name),
172 quote_identifier(&grant.grantee)
173 );
174 self.client.execute(&sql, &[]).await.map_err(|e| {
175 ConnectionError::Message(format!(
176 "Failed to grant {} to {} on cluster '{}': {}",
177 grant.privilege_type, grant.grantee, name, e
178 ))
179 })?;
180 }
181
182 Ok(())
183 }
184}