1use std::net::SocketAddr;
16use std::time::Duration;
17
18use client::error::ServerSnafu;
19use client::{
20 Client, DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, Database, OutputData, RecordBatches,
21};
22use common_error::ext::ErrorExt;
23use common_query::Output;
24use mysql::prelude::Queryable;
25use mysql::{Conn as MySqlClient, Row as MySqlRow};
26use tokio_postgres::{Client as PgClient, SimpleQueryMessage as PgRow};
27
28use crate::util::retry_with_backoff;
29
30pub struct MultiProtocolClient {
32 grpc_client: Database,
33 pg_client: PgClient,
34 mysql_client: MySqlClient,
35}
36
37pub enum MysqlSqlResult {
39 AffectedRows(u64),
40 Rows(Vec<MySqlRow>),
41}
42
43impl MultiProtocolClient {
44 pub async fn connect(
60 grpc_server_addr: &str,
61 pg_server_addr: &str,
62 mysql_server_addr: &str,
63 ) -> MultiProtocolClient {
64 let grpc_client = Database::new(
65 DEFAULT_CATALOG_NAME,
66 DEFAULT_SCHEMA_NAME,
67 Client::with_query_and_control_managers(
68 Default::default(),
69 Default::default(),
70 vec![grpc_server_addr],
71 ),
72 );
73 let pg_client = create_postgres_client(pg_server_addr).await;
74 let mysql_client = create_mysql_client(mysql_server_addr).await;
75 MultiProtocolClient {
76 grpc_client,
77 pg_client,
78 mysql_client,
79 }
80 }
81
82 pub async fn reconnect_mysql_client(&mut self, mysql_server_addr: &str) {
84 self.mysql_client = create_mysql_client(mysql_server_addr).await;
85 }
86
87 pub async fn reconnect_pg_client(&mut self, pg_server_addr: &str) {
89 self.pg_client = create_postgres_client(pg_server_addr).await;
90 }
91
92 pub async fn postgres_query(&mut self, query: &str) -> Result<Vec<PgRow>, String> {
94 match self.pg_client.simple_query(query).await {
95 Ok(rows) => Ok(rows),
96 Err(e) => Err(format!("Failed to execute query, encountered: {:?}", e)),
97 }
98 }
99
100 pub async fn mysql_query(&mut self, query: &str) -> Result<MysqlSqlResult, String> {
102 let result = self.mysql_client.query_iter(query);
103 match result {
104 Ok(result) => {
105 let mut rows = vec![];
106 let affected_rows = result.affected_rows();
107 for row in result {
108 match row {
109 Ok(r) => rows.push(r),
110 Err(e) => {
111 return Err(format!("Failed to parse query result, err: {:?}", e));
112 }
113 }
114 }
115
116 if rows.is_empty() {
117 Ok(MysqlSqlResult::AffectedRows(affected_rows))
118 } else {
119 Ok(MysqlSqlResult::Rows(rows))
120 }
121 }
122 Err(e) => Err(format!("Failed to execute query, err: {:?}", e)),
123 }
124 }
125
126 pub async fn grpc_query(&mut self, query: &str) -> Result<Output, client::Error> {
128 let query_str = query.trim().to_lowercase();
129 if query_str.starts_with("use ") {
130 let database = query
132 .split_ascii_whitespace()
133 .nth(1)
134 .expect("Illegal `USE` statement: expecting a database.")
135 .trim_end_matches(';');
136 self.grpc_client.set_schema(database);
137 Ok(Output::new_with_affected_rows(0))
138 } else if query_str.starts_with("set time_zone")
139 || query_str.starts_with("set session time_zone")
140 || query_str.starts_with("set local time_zone")
141 {
142 let timezone = query
144 .split('=')
145 .nth(1)
146 .expect("Illegal `SET TIMEZONE` statement: expecting a timezone expr.")
147 .trim()
148 .strip_prefix('\'')
149 .unwrap()
150 .strip_suffix("';")
151 .unwrap();
152
153 self.grpc_client.set_timezone(timezone);
154 Ok(Output::new_with_affected_rows(0))
155 } else {
156 let mut result = self.grpc_client.sql(&query).await;
157 if let Ok(Output {
158 data: OutputData::Stream(stream),
159 ..
160 }) = result
161 {
162 match RecordBatches::try_collect(stream).await {
163 Ok(recordbatches) => {
164 result = Ok(Output::new_with_record_batches(recordbatches));
165 }
166 Err(e) => {
167 let status_code = e.status_code();
168 let msg = e.output_msg();
169 result = ServerSnafu {
170 code: status_code,
171 msg,
172 }
173 .fail();
174 }
175 }
176 }
177
178 result
179 }
180 }
181}
182
183async fn create_postgres_client(pg_server_addr: &str) -> PgClient {
189 let sockaddr: SocketAddr = pg_server_addr.parse().unwrap_or_else(|_| {
190 panic!("Failed to parse the Postgres server address {pg_server_addr}. Please check if the address is in the format of `ip:port`.")
191 });
192 let mut config = tokio_postgres::config::Config::new();
193 config.host(sockaddr.ip().to_string());
194 config.port(sockaddr.port());
195 config.dbname(DEFAULT_SCHEMA_NAME);
196
197 retry_with_backoff(
198 || async {
199 config
200 .connect(tokio_postgres::NoTls)
201 .await
202 .map(|(client, conn)| {
203 tokio::spawn(conn);
204 client
205 })
206 },
207 3,
208 Duration::from_millis(500),
209 )
210 .await
211 .unwrap_or_else(|_| {
212 panic!("Failed to connect to Postgres server. Please check if the server is running.")
213 })
214}
215
216async fn create_mysql_client(mysql_server_addr: &str) -> MySqlClient {
222 let sockaddr: SocketAddr = mysql_server_addr.parse().unwrap_or_else(|_| {
223 panic!("Failed to parse the MySQL server address {mysql_server_addr}. Please check if the address is in the format of `ip:port`.")
224 });
225 let ops = mysql::OptsBuilder::new()
226 .ip_or_hostname(Some(sockaddr.ip().to_string()))
227 .tcp_port(sockaddr.port())
228 .db_name(Some(DEFAULT_SCHEMA_NAME));
229
230 retry_with_backoff(
231 || async { mysql::Conn::new(ops.clone()) },
232 3,
233 Duration::from_millis(500),
234 )
235 .await
236 .unwrap_or_else(|_| {
237 panic!("Failed to connect to MySQL server. Please check if the server is running.")
238 })
239}