1use std::collections::HashMap;
16use std::net::SocketAddr;
17use std::sync::Arc;
18use std::sync::atomic::{AtomicU32, Ordering};
19use std::time::Duration;
20
21use ::auth::{Identity, Password, UserProviderRef};
22use async_trait::async_trait;
23use chrono::{NaiveDate, NaiveDateTime};
24use common_catalog::parse_optional_catalog_and_schema_from_db_string;
25use common_error::ext::ErrorExt;
26use common_query::Output;
27use common_telemetry::{debug, error, tracing, warn};
28use common_time::Timezone;
29use datafusion_common::ParamValues;
30use datafusion_expr::LogicalPlan;
31use datatypes::prelude::ConcreteDataType;
32use datatypes::schema::Schema;
33use itertools::Itertools;
34use mysql_common::Value as MysqlValue;
35use opensrv_mysql::{
36 AsyncMysqlShim, Column, ErrorKind, InitWriter, ParamParser, ParamValue, QueryResultWriter,
37 StatementMetaWriter, ValueInner,
38};
39use parking_lot::RwLock;
40use query::planner::DfLogicalPlanner;
41use query::query_engine::DescribeResult;
42use rand::RngCore;
43use session::context::{Channel, QueryContextRef};
44use session::{Session, SessionRef};
45use snafu::{ResultExt, ensure};
46use sql::dialect::MySqlDialect;
47use sql::parser::{ParseOptions, ParserContext};
48use sql::statements::statement::Statement;
49use tokio::io::AsyncWrite;
50
51use crate::SqlPlan;
52use crate::error::{
53 self, DataFrameSnafu, InferParameterTypesSnafu, InvalidPrepareStatementSnafu, Result,
54};
55use crate::metrics::METRIC_AUTH_FAILURE;
56use crate::mysql::helper::{self, format_placeholder, transform_placeholders_with_count};
57use crate::mysql::writer;
58use crate::mysql::writer::{create_mysql_column, handle_err};
59use crate::query_handler::sql::ServerSqlQueryHandlerRef;
60
61const MYSQL_NATIVE_PASSWORD: &str = "mysql_native_password";
62const MYSQL_CLEAR_PASSWORD: &str = "mysql_clear_password";
63
64enum Params<'a> {
66 ProtocolParams(Vec<ParamValue<'a>>),
68 CliParams(Vec<sql::ast::Expr>),
70}
71
72impl Params<'_> {
73 fn len(&self) -> usize {
74 match self {
75 Params::ProtocolParams(params) => params.len(),
76 Params::CliParams(params) => params.len(),
77 }
78 }
79}
80
81pub struct MysqlInstanceShim {
83 query_handler: ServerSqlQueryHandlerRef,
84 salt: [u8; 20],
85 session: SessionRef,
86 user_provider: Option<UserProviderRef>,
87 prepared_stmts: Arc<RwLock<HashMap<String, SqlPlan>>>,
88 prepared_stmts_counter: AtomicU32,
89 process_id: u32,
90 prepared_stmt_cache_size: usize,
91}
92
93impl MysqlInstanceShim {
94 pub fn create(
95 query_handler: ServerSqlQueryHandlerRef,
96 user_provider: Option<UserProviderRef>,
97 client_addr: SocketAddr,
98 process_id: u32,
99 prepared_stmt_cache_size: usize,
100 ) -> MysqlInstanceShim {
101 let mut bs = vec![0u8; 20];
103 let mut rng = rand::rng();
104 rng.fill_bytes(bs.as_mut());
105
106 let mut scramble: [u8; 20] = [0; 20];
107 for i in 0..20 {
108 scramble[i] = bs[i] & 0x7fu8;
109 if scramble[i] == b'\0' || scramble[i] == b'$' {
110 scramble[i] += 1;
111 }
112 }
113
114 MysqlInstanceShim {
115 query_handler,
116 salt: scramble,
117 session: Arc::new(Session::new(
118 Some(client_addr),
119 Channel::Mysql,
120 Default::default(),
121 process_id,
122 )),
123 user_provider,
124 prepared_stmts: Default::default(),
125 prepared_stmts_counter: AtomicU32::new(1),
126 process_id,
127 prepared_stmt_cache_size,
128 }
129 }
130
131 #[tracing::instrument(skip_all, name = "mysql::do_query")]
132 async fn do_query(&self, query: &str, query_ctx: QueryContextRef) -> Vec<Result<Output>> {
133 if let Some(output) =
134 crate::mysql::federated::check(query, query_ctx.clone(), self.session.clone())
135 {
136 vec![Ok(output)]
137 } else {
138 self.query_handler.do_query(query, query_ctx.clone()).await
139 }
140 }
141
142 async fn do_describe(
144 &self,
145 statement: Statement,
146 query_ctx: QueryContextRef,
147 ) -> Result<Option<DescribeResult>> {
148 self.query_handler.do_describe(statement, query_ctx).await
149 }
150
151 fn save_plan(&self, plan: SqlPlan, stmt_key: String) -> Result<()> {
153 let mut prepared_stmts = self.prepared_stmts.write();
154 let max_capacity = self.prepared_stmt_cache_size;
155
156 let is_update = prepared_stmts.contains_key(&stmt_key);
157
158 if !is_update && prepared_stmts.len() >= max_capacity {
159 return error::InternalSnafu {
160 err_msg: format!(
161 "Prepared statement cache is full, max capacity: {}",
162 max_capacity
163 ),
164 }
165 .fail();
166 }
167
168 let _ = prepared_stmts.insert(stmt_key, plan);
169 Ok(())
170 }
171
172 fn plan(&self, stmt_key: &str) -> Option<SqlPlan> {
174 let guard = self.prepared_stmts.read();
175 guard.get(stmt_key).cloned()
176 }
177
178 async fn do_prepare(
180 &mut self,
181 raw_query: &str,
182 query_ctx: QueryContextRef,
183 stmt_key: String,
184 ) -> Result<(Vec<Column>, Vec<Column>)> {
185 if crate::mysql::federated::check(raw_query, query_ctx.clone(), self.session.clone())
186 .is_some()
187 {
188 self.save_plan(SqlPlan::Shortcut(raw_query.to_string()), stmt_key)
189 .inspect_err(|e| {
190 error!(e; "Failed to save prepared statement");
191 })?;
192 return Ok((vec![], vec![]));
193 }
194
195 let statement = validate_query(raw_query).await?;
196
197 let (statement, placeholder_count) = transform_placeholders_with_count(statement);
200 let param_num = placeholder_count + 1;
201
202 let describe_result = self
203 .do_describe(statement.clone(), query_ctx.clone())
204 .await?;
205 let plan = describe_result.map(|DescribeResult { logical_plan }| logical_plan);
206
207 let (params, can_cache_as_plan) = if let Some(plan) = &plan {
208 let param_types = DfLogicalPlanner::get_inferred_parameter_types(plan)
209 .context(InferParameterTypesSnafu)?
210 .into_iter()
211 .map(|(k, v)| (k, v.map(|v| ConcreteDataType::from_arrow_type(&v))))
212 .collect();
213
214 (
215 prepared_params(¶m_types, param_num)?,
216 all_params_have_types(¶m_types, param_num),
217 )
218 } else {
219 (dummy_params(param_num)?, false)
220 };
221
222 let columns =
223 plan.as_ref()
224 .map(|plan| {
225 let schema: Schema = plan.schema().clone().try_into().map_err(
226 |e: datatypes::error::Error| {
227 error::InternalSnafu {
228 err_msg: e.to_string(),
229 }
230 .build()
231 },
232 )?;
233 schema
234 .column_schemas()
235 .iter()
236 .map(|column_schema| {
237 create_mysql_column(&column_schema.data_type, &column_schema.name)
238 })
239 .collect::<Result<Vec<_>>>()
240 })
241 .transpose()?
242 .unwrap_or_default();
243
244 match plan {
245 Some(plan) if can_cache_as_plan => {
246 self.save_plan(SqlPlan::Plan(plan, statement), stmt_key)
247 .inspect_err(|e| {
248 error!(e; "Failed to save prepared statement");
249 })?;
250 }
251 _ => {
252 self.save_plan(
253 SqlPlan::Statement(statement, raw_query.to_string()),
254 stmt_key,
255 )
256 .inspect_err(|e| {
257 error!(e; "Failed to save prepared statement");
258 })?;
259 }
260 }
261
262 Ok((params, columns))
263 }
264
265 async fn do_execute(
266 &mut self,
267 query_ctx: QueryContextRef,
268 stmt_key: String,
269 params: Params<'_>,
270 ) -> Result<Vec<std::result::Result<Output, error::Error>>> {
271 let sql_plan = match self.plan(&stmt_key) {
272 None => {
273 return error::PrepareStatementNotFoundSnafu { name: stmt_key }.fail();
274 }
275 Some(sql_plan) => sql_plan,
276 };
277
278 let outputs = match sql_plan {
279 SqlPlan::Plan(plan, stmt) => {
280 let param_types = DfLogicalPlanner::get_inferred_parameter_types(&plan)
281 .context(InferParameterTypesSnafu)?
282 .into_iter()
283 .map(|(k, v)| (k, v.map(|v| ConcreteDataType::from_arrow_type(&v))))
284 .collect::<HashMap<_, _>>();
285
286 if params.len() != param_types.len() {
287 return error::InternalSnafu {
288 err_msg: "Prepare statement params number mismatch".to_string(),
289 }
290 .fail();
291 }
292
293 let timezone = query_ctx.timezone();
294 let replaced_plan = match params {
295 Params::ProtocolParams(params) => {
296 replace_params_with_values(&plan, param_types, ¶ms, &timezone)
297 }
298 Params::CliParams(params) => {
299 replace_params_with_exprs(&plan, param_types, ¶ms, &timezone)
300 }
301 }?;
302
303 debug!(
304 "Mysql execute prepared plan: {}",
305 replaced_plan.display_indent()
306 );
307 vec![
308 self.query_handler
309 .do_exec_plan(replaced_plan, Some(stmt), query_ctx.clone())
310 .await,
311 ]
312 }
313 SqlPlan::Shortcut(query) => {
314 if let Some(output) =
315 crate::mysql::federated::check(&query, query_ctx.clone(), self.session.clone())
316 {
317 vec![Ok(output)]
318 } else {
319 self.do_query(&query, query_ctx.clone()).await
320 }
321 }
322 SqlPlan::Statement(stmt, query) => {
323 let param_strs = match params {
324 Params::ProtocolParams(params) => {
325 params.iter().map(convert_param_value_to_string).collect()
326 }
327 Params::CliParams(params) => params.iter().map(|x| x.to_string()).collect(),
328 };
329 debug!(
330 "do_execute Replacing with Params: {:?}, Original Query: {}",
331 param_strs, query
332 );
333 let query = replace_params(param_strs, stmt, query)?;
334 debug!("Mysql execute replaced query: {}", query);
335 self.do_query(&query, query_ctx.clone()).await
336 }
337 _ => {
338 return error::PrepareStatementNotFoundSnafu { name: stmt_key }.fail();
339 }
340 };
341
342 Ok(outputs)
343 }
344
345 fn do_close(&mut self, stmt_key: String) {
347 let mut guard = self.prepared_stmts.write();
348 let _ = guard.remove(&stmt_key);
349 }
350
351 fn auth_plugin(&self) -> &'static str {
352 if self
353 .user_provider
354 .as_ref()
355 .map(|x| x.external())
356 .unwrap_or(false)
357 {
358 MYSQL_CLEAR_PASSWORD
359 } else {
360 MYSQL_NATIVE_PASSWORD
361 }
362 }
363}
364
365#[async_trait]
366impl<W: AsyncWrite + Send + Sync + Unpin> AsyncMysqlShim<W> for MysqlInstanceShim {
367 type Error = error::Error;
368
369 fn version(&self) -> String {
370 std::env::var("GREPTIMEDB_MYSQL_SERVER_VERSION").unwrap_or_else(|_| "8.4.2".to_string())
371 }
372
373 fn connect_id(&self) -> u32 {
374 self.process_id
375 }
376
377 fn default_auth_plugin(&self) -> &str {
378 self.auth_plugin()
379 }
380
381 async fn auth_plugin_for_username(&self, _user: &[u8]) -> &'static str {
382 self.auth_plugin()
383 }
384
385 fn salt(&self) -> [u8; 20] {
386 self.salt
387 }
388
389 async fn authenticate(
390 &self,
391 auth_plugin: &str,
392 username: &[u8],
393 salt: &[u8],
394 auth_data: &[u8],
395 ) -> bool {
396 let username = String::from_utf8_lossy(username);
398
399 let mut user_info = None;
400 let addr = self
401 .session
402 .conn_info()
403 .client_addr
404 .map(|addr| addr.to_string());
405 if let Some(user_provider) = &self.user_provider {
406 let user_id = Identity::UserId(&username, addr.as_deref());
407
408 let password = match auth_plugin {
409 MYSQL_NATIVE_PASSWORD => Password::MysqlNativePassword(auth_data, salt),
410 MYSQL_CLEAR_PASSWORD => {
411 let password = if let &[password @ .., 0] = &auth_data {
414 password
415 } else {
416 auth_data
417 };
418 Password::PlainText(String::from_utf8_lossy(password).to_string().into())
419 }
420 other => {
421 error!("Unsupported mysql auth plugin: {}", other);
422 return false;
423 }
424 };
425 match user_provider.authenticate(user_id, password).await {
426 Ok(userinfo) => {
427 user_info = Some(userinfo);
428 }
429 Err(e) => {
430 METRIC_AUTH_FAILURE
431 .with_label_values(&[e.status_code().as_ref()])
432 .inc();
433 warn!(e; "Failed to auth");
434 return false;
435 }
436 };
437 }
438 let user_info =
439 user_info.unwrap_or_else(|| auth::userinfo_by_name(Some(username.to_string())));
440
441 self.session.set_user_info(user_info);
442
443 true
444 }
445
446 async fn on_prepare<'a>(
447 &'a mut self,
448 raw_query: &'a str,
449 w: StatementMetaWriter<'a, W>,
450 ) -> Result<()> {
451 let query_ctx = self.session.new_query_context();
452 let stmt_id = self.prepared_stmts_counter.fetch_add(1, Ordering::Relaxed);
453 let stmt_key = uuid::Uuid::from_u128(stmt_id as u128).to_string();
454 let (params, columns) = match self
455 .do_prepare(raw_query, query_ctx.clone(), stmt_key)
456 .await
457 {
458 Ok(x) => x,
459 Err(e) => {
460 let (kind, msg) = handle_err(e, query_ctx.clone());
461 w.error(kind, msg.as_bytes()).await?;
462 return Ok(());
463 }
464 };
465 debug!("on_prepare: Params: {:?}, Columns: {:?}", params, columns);
466 w.reply(stmt_id, ¶ms, &columns).await?;
467 crate::metrics::METRIC_MYSQL_PREPARED_COUNT
468 .with_label_values(&[query_ctx.get_db_string().as_str()])
469 .inc();
470 return Ok(());
471 }
472
473 async fn on_execute<'a>(
474 &'a mut self,
475 stmt_id: u32,
476 p: ParamParser<'a>,
477 w: QueryResultWriter<'a, W>,
478 ) -> Result<()> {
479 self.session.clear_warnings();
480
481 let query_ctx = self.session.new_query_context();
482 let db = query_ctx.get_db_string();
483 let _timer = crate::metrics::METRIC_MYSQL_QUERY_TIMER
484 .with_label_values(&[crate::metrics::METRIC_MYSQL_BINQUERY, db.as_str()])
485 .start_timer();
486
487 let params: Vec<ParamValue> = p.into_iter().collect();
488 let stmt_key = uuid::Uuid::from_u128(stmt_id as u128).to_string();
489
490 let outputs = match self
491 .do_execute(query_ctx.clone(), stmt_key, Params::ProtocolParams(params))
492 .await
493 {
494 Ok(outputs) => outputs,
495 Err(e) => {
496 let (kind, err) = handle_err(e, query_ctx);
497 debug!(
498 "Failed to execute prepared statement, kind: {:?}, err: {}",
499 kind, err
500 );
501 w.error(kind, err.as_bytes()).await?;
502 return Ok(());
503 }
504 };
505
506 writer::write_output(w, query_ctx, self.session.clone(), outputs).await?;
507
508 Ok(())
509 }
510
511 async fn on_close<'a>(&'a mut self, stmt_id: u32)
512 where
513 W: 'async_trait,
514 {
515 let stmt_key = uuid::Uuid::from_u128(stmt_id as u128).to_string();
516 self.do_close(stmt_key);
517 }
518
519 #[tracing::instrument(skip_all, fields(protocol = "mysql"))]
520 async fn on_query<'a>(
521 &'a mut self,
522 query: &'a str,
523 writer: QueryResultWriter<'a, W>,
524 ) -> Result<()> {
525 let query_ctx = self.session.new_query_context();
526 let db = query_ctx.get_db_string();
527 let _timer = crate::metrics::METRIC_MYSQL_QUERY_TIMER
528 .with_label_values(&[crate::metrics::METRIC_MYSQL_TEXTQUERY, db.as_str()])
529 .start_timer();
530
531 let query_upcase = query.to_uppercase();
533 if !query_upcase.starts_with("SHOW WARNINGS") {
534 self.session.clear_warnings();
535 }
536
537 if query_upcase.starts_with("PREPARE ") {
538 match ParserContext::parse_mysql_prepare_stmt(query, query_ctx.sql_dialect()) {
539 Ok((stmt_name, stmt)) => {
540 let prepare_results =
541 self.do_prepare(&stmt, query_ctx.clone(), stmt_name).await;
542 match prepare_results {
543 Ok(_) => {
544 let outputs = vec![Ok(Output::new_with_affected_rows(0))];
545 writer::write_output(writer, query_ctx, self.session.clone(), outputs)
546 .await?;
547 return Ok(());
548 }
549 Err(e) => {
550 writer
551 .error(ErrorKind::ER_SP_BADSTATEMENT, e.output_msg().as_bytes())
552 .await?;
553 return Ok(());
554 }
555 }
556 }
557 Err(e) => {
558 writer
559 .error(ErrorKind::ER_PARSE_ERROR, e.output_msg().as_bytes())
560 .await?;
561 return Ok(());
562 }
563 }
564 } else if query_upcase.starts_with("EXECUTE ") {
565 match ParserContext::parse_mysql_execute_stmt(query, query_ctx.sql_dialect()) {
566 Ok((stmt_name, params)) => {
567 let outputs = match self
568 .do_execute(query_ctx.clone(), stmt_name, Params::CliParams(params))
569 .await
570 {
571 Ok(outputs) => outputs,
572 Err(e) => {
573 let (kind, err) = handle_err(e, query_ctx);
574 debug!(
575 "Failed to execute prepared statement, kind: {:?}, err: {}",
576 kind, err
577 );
578 writer.error(kind, err.as_bytes()).await?;
579 return Ok(());
580 }
581 };
582 writer::write_output(writer, query_ctx, self.session.clone(), outputs).await?;
583
584 return Ok(());
585 }
586 Err(e) => {
587 writer
588 .error(ErrorKind::ER_PARSE_ERROR, e.output_msg().as_bytes())
589 .await?;
590 return Ok(());
591 }
592 }
593 } else if query_upcase.starts_with("DEALLOCATE ") {
594 match ParserContext::parse_mysql_deallocate_stmt(query, query_ctx.sql_dialect()) {
595 Ok(stmt_name) => {
596 self.do_close(stmt_name);
597 let outputs = vec![Ok(Output::new_with_affected_rows(0))];
598 writer::write_output(writer, query_ctx, self.session.clone(), outputs).await?;
599 return Ok(());
600 }
601 Err(e) => {
602 writer
603 .error(ErrorKind::ER_PARSE_ERROR, e.output_msg().as_bytes())
604 .await?;
605 return Ok(());
606 }
607 }
608 }
609
610 let outputs = self.do_query(query, query_ctx.clone()).await;
611 writer::write_output(writer, query_ctx, self.session.clone(), outputs).await?;
612
613 Ok(())
614 }
615
616 async fn on_init<'a>(&'a mut self, database: &'a str, w: InitWriter<'a, W>) -> Result<()> {
617 let (catalog_from_db, schema) = parse_optional_catalog_and_schema_from_db_string(database);
618 let catalog = if let Some(catalog) = &catalog_from_db {
619 catalog.clone()
620 } else {
621 self.session.catalog()
622 };
623
624 if !self
625 .query_handler
626 .is_valid_schema(&catalog, &schema)
627 .await?
628 {
629 return w
630 .error(
631 ErrorKind::ER_WRONG_DB_NAME,
632 format!("Unknown database '{}'", database).as_bytes(),
633 )
634 .await
635 .map_err(|e| e.into());
636 }
637
638 let user_info = &self.session.user_info();
639
640 if let Some(schema_validator) = &self.user_provider
641 && let Err(e) = schema_validator
642 .authorize(&catalog, &schema, user_info)
643 .await
644 {
645 METRIC_AUTH_FAILURE
646 .with_label_values(&[e.status_code().as_ref()])
647 .inc();
648 return w
649 .error(
650 ErrorKind::ER_DBACCESS_DENIED_ERROR,
651 e.output_msg().as_bytes(),
652 )
653 .await
654 .map_err(|e| e.into());
655 }
656
657 if catalog_from_db.is_some() {
658 self.session.set_catalog(catalog)
659 }
660 self.session.set_schema(schema);
661
662 w.ok().await.map_err(|e| e.into())
663 }
664}
665
666fn convert_param_value_to_string(param: &ParamValue) -> String {
667 match param.value.into_inner() {
668 ValueInner::Int(u) => u.to_string(),
669 ValueInner::UInt(u) => u.to_string(),
670 ValueInner::Double(u) => u.to_string(),
671 ValueInner::NULL => "NULL".to_string(),
672 ValueInner::Bytes(b) => MysqlValue::Bytes(b.to_vec()).as_sql(false),
677 ValueInner::Date(_) => format!("'{}'", NaiveDate::from(param.value)),
678 ValueInner::Datetime(_) => format!("'{}'", NaiveDateTime::from(param.value)),
679 ValueInner::Time(_) => format_duration(Duration::from(param.value)),
680 }
681}
682
683fn replace_params(params: Vec<String>, stmt: Statement, mut query: String) -> Result<String> {
684 let spans = helper::placeholder_spans(stmt);
685 ensure!(
686 spans.len() == params.len(),
687 error::InternalSnafu {
688 err_msg: format!(
689 "Prepared statement expected {} parameters but got {}",
690 spans.len(),
691 params.len()
692 )
693 }
694 );
695
696 let mut replacements = Vec::with_capacity(spans.len());
697 for span in spans {
698 let start = location_to_byte_offset(&query, span.start_line, span.start_column)
699 .ok_or_else(|| {
700 error::InternalSnafu {
701 err_msg: format!(
702 "Invalid placeholder start span: line {}, column {}",
703 span.start_line, span.start_column
704 ),
705 }
706 .build()
707 })?;
708 let end =
709 location_to_byte_offset(&query, span.end_line, span.end_column).ok_or_else(|| {
710 error::InternalSnafu {
711 err_msg: format!(
712 "Invalid placeholder end span: line {}, column {}",
713 span.end_line, span.end_column
714 ),
715 }
716 .build()
717 })?;
718 let param = span
719 .index
720 .checked_sub(1)
721 .and_then(|idx| params.get(idx))
722 .ok_or_else(|| {
723 error::InternalSnafu {
724 err_msg: format!("Missing prepared statement parameter {}", span.index),
725 }
726 .build()
727 })?;
728
729 ensure!(
730 start < end && end <= query.len(),
731 error::InternalSnafu {
732 err_msg: format!(
733 "Invalid placeholder byte span: {}..{} for query length {}",
734 start,
735 end,
736 query.len()
737 )
738 }
739 );
740 ensure!(
741 query.get(start..end) == Some("?"),
742 error::InternalSnafu {
743 err_msg: format!(
744 "Prepared statement placeholder span maps to {:?} instead of '?'",
745 query.get(start..end)
746 )
747 }
748 );
749
750 replacements.push((start, end, param.clone()));
751 }
752
753 replacements.sort_unstable_by_key(|(start, _, _)| *start);
754 for windows in replacements.windows(2) {
755 ensure!(
756 windows[0].1 <= windows[1].0,
757 error::InternalSnafu {
758 err_msg: "Overlapping placeholder spans in prepared statement".to_string()
759 }
760 );
761 }
762
763 for (start, end, param) in replacements.into_iter().rev() {
767 query.replace_range(start..end, ¶m);
768 }
769
770 Ok(query)
771}
772
773fn location_to_byte_offset(query: &str, line: u64, column: u64) -> Option<usize> {
774 if line == 0 || column == 0 {
778 return None;
779 }
780
781 let mut current_line = 1;
782 let mut current_column = 1;
783 for (index, ch) in query.char_indices() {
784 if current_line == line && current_column == column {
785 return Some(index);
786 }
787
788 if ch == '\n' {
789 current_line += 1;
790 current_column = 1;
791 } else {
792 current_column += 1;
793 }
794 }
795
796 (current_line == line && current_column == column).then_some(query.len())
799}
800
801fn format_duration(duration: Duration) -> String {
802 let seconds = duration.as_secs() % 60;
803 let minutes = (duration.as_secs() / 60) % 60;
804 let hours = (duration.as_secs() / 60) / 60;
805 format!("'{}:{}:{}'", hours, minutes, seconds)
806}
807
808fn replace_params_with_values(
809 plan: &LogicalPlan,
810 param_types: HashMap<String, Option<ConcreteDataType>>,
811 params: &[ParamValue],
812 timezone: &Timezone,
813) -> Result<LogicalPlan> {
814 debug_assert_eq!(param_types.len(), params.len());
815
816 debug!(
817 "replace_params_with_values(param_types: {:#?}, params: {:#?}, plan: {:#?})",
818 param_types,
819 params
820 .iter()
821 .map(|x| format!("({:?}, {:?})", x.value, x.coltype))
822 .join(", "),
823 plan
824 );
825
826 let mut values = Vec::with_capacity(params.len());
827
828 for (i, param) in params.iter().enumerate() {
829 if let Some(Some(t)) = param_types.get(&format_placeholder(i + 1)) {
830 let value = helper::convert_value(param, t, timezone)?;
831
832 values.push(value.into());
833 }
834 }
835
836 plan.clone()
837 .replace_params_with_values(&ParamValues::List(values.clone()))
838 .context(DataFrameSnafu)
839}
840
841fn replace_params_with_exprs(
842 plan: &LogicalPlan,
843 param_types: HashMap<String, Option<ConcreteDataType>>,
844 params: &[sql::ast::Expr],
845 timezone: &Timezone,
846) -> Result<LogicalPlan> {
847 debug_assert_eq!(param_types.len(), params.len());
848
849 debug!(
850 "replace_params_with_exprs(param_types: {:#?}, params: {:#?}, plan: {:#?})",
851 param_types,
852 params.iter().map(|x| format!("({:?})", x)).join(", "),
853 plan
854 );
855
856 let mut values = Vec::with_capacity(params.len());
857
858 for (i, param) in params.iter().enumerate() {
859 if let Some(Some(t)) = param_types.get(&format_placeholder(i + 1)) {
860 let value = helper::convert_expr_to_scalar_value(param, t, timezone)?;
861
862 values.push(value.into());
863 }
864 }
865
866 plan.clone()
867 .replace_params_with_values(&ParamValues::List(values.clone()))
868 .context(DataFrameSnafu)
869}
870
871async fn validate_query(query: &str) -> Result<Statement> {
872 let statement =
873 ParserContext::create_with_dialect(query, &MySqlDialect {}, ParseOptions::default());
874 let mut statement = statement.map_err(|e| {
875 InvalidPrepareStatementSnafu {
876 err_msg: e.output_msg(),
877 }
878 .build()
879 })?;
880
881 ensure!(
882 statement.len() == 1,
883 InvalidPrepareStatementSnafu {
884 err_msg: "prepare statement only support single statement".to_string(),
885 }
886 );
887
888 let statement = statement.remove(0);
889
890 Ok(statement)
891}
892
893fn dummy_params(index: usize) -> Result<Vec<Column>> {
894 let mut params = Vec::with_capacity(index - 1);
895
896 for _ in 1..index {
897 params.push(create_mysql_column(&ConcreteDataType::null_datatype(), "")?);
898 }
899
900 Ok(params)
901}
902
903fn prepared_params(
905 param_types: &HashMap<String, Option<ConcreteDataType>>,
906 param_num: usize,
907) -> Result<Vec<Column>> {
908 let mut params = Vec::with_capacity(param_num - 1);
909
910 for i in 1..param_num {
912 let column = if let Some(Some(t)) = param_types.get(&format_placeholder(i)) {
913 create_mysql_column(t, "")?
914 } else {
915 create_mysql_column(&ConcreteDataType::null_datatype(), "")?
916 };
917 params.push(column);
918 }
919
920 Ok(params)
921}
922
923fn all_params_have_types(
924 param_types: &HashMap<String, Option<ConcreteDataType>>,
925 param_num: usize,
926) -> bool {
927 param_types.len() == param_num - 1
928 && (1..param_num).all(|i| matches!(param_types.get(&format_placeholder(i)), Some(Some(_))))
929}
930
931#[cfg(test)]
932mod tests {
933 use std::sync::Arc;
934
935 use async_trait::async_trait;
936 use common_query::Output;
937 use datafusion_expr::LogicalPlan;
938 use query::parser::PromQuery;
939 use query::query_engine::DescribeResult;
940 use session::context::QueryContext;
941 use sql::statements::statement::Statement;
942
943 use super::*;
944 use crate::error::Result;
945 use crate::query_handler::sql::SqlQueryHandler;
946
947 struct DummyQueryHandler;
948
949 #[async_trait]
950 impl SqlQueryHandler for DummyQueryHandler {
951 async fn do_query(&self, _: &str, _: QueryContextRef) -> Vec<Result<Output>> {
952 unimplemented!()
953 }
954
955 async fn do_analyze_stream_query(&self, _: &str, _: QueryContextRef) -> Result<Output> {
956 unimplemented!()
957 }
958
959 async fn do_promql_query(&self, _: &PromQuery, _: QueryContextRef) -> Vec<Result<Output>> {
960 unimplemented!()
961 }
962
963 async fn do_exec_plan(
964 &self,
965 _: LogicalPlan,
966 _: Option<Statement>,
967 _: QueryContextRef,
968 ) -> Result<Output> {
969 unimplemented!()
970 }
971
972 async fn do_describe(
973 &self,
974 _: Statement,
975 _: QueryContextRef,
976 ) -> Result<Option<DescribeResult>> {
977 unimplemented!()
978 }
979
980 async fn is_valid_schema(&self, _: &str, _: &str) -> Result<bool> {
981 Ok(true)
982 }
983 }
984
985 fn create_shim() -> MysqlInstanceShim {
986 MysqlInstanceShim::create(
987 Arc::new(DummyQueryHandler),
988 None,
989 "127.0.0.1:3306".parse().unwrap(),
990 1,
991 1024,
992 )
993 }
994
995 fn statement_with_transformed_placeholders(query: &str) -> Statement {
996 let mut statements =
997 ParserContext::create_with_dialect(query, &MySqlDialect {}, ParseOptions::default())
998 .unwrap();
999 assert_eq!(statements.len(), 1);
1000 transform_placeholders_with_count(statements.remove(0)).0
1001 }
1002
1003 #[test]
1004 fn test_prepared_params_keep_unknown_type_placeholders() {
1005 let mut param_types = HashMap::new();
1006 param_types.insert(format_placeholder(1), None);
1007 param_types.insert(
1008 format_placeholder(2),
1009 Some(ConcreteDataType::int32_datatype()),
1010 );
1011
1012 let params = prepared_params(¶m_types, 3).unwrap();
1013 assert_eq!(params.len(), 2);
1014 assert!(!all_params_have_types(¶m_types, 3));
1015 }
1016
1017 #[test]
1018 fn test_replace_params_by_placeholder_span() {
1019 let query = "SELECT ?, ?".to_string();
1020 let stmt = statement_with_transformed_placeholders(&query);
1021 let params = vec!["'$2 should stay'".to_string(), "'value'".to_string()];
1022
1023 assert_eq!(
1024 "SELECT '$2 should stay', 'value'",
1025 replace_params(params, stmt, query).unwrap()
1026 );
1027
1028 let query = "SELECT ?, ?, ?".to_string();
1029 let stmt = statement_with_transformed_placeholders(&query);
1030 let params = vec![
1031 "'much longer than a placeholder'".to_string(),
1032 "0".to_string(),
1033 "'also much longer than a placeholder'".to_string(),
1034 ];
1035
1036 assert_eq!(
1037 "SELECT 'much longer than a placeholder', 0, 'also much longer than a placeholder'",
1038 replace_params(params, stmt, query).unwrap()
1039 );
1040
1041 let query = "SELECT '$1', \"$2\", `$3`, ?, ?".to_string();
1042 let stmt = statement_with_transformed_placeholders(&query);
1043 let params = vec!["'1'".to_string(), "'2'".to_string()];
1044
1045 assert_eq!(
1046 "SELECT '$1', \"$2\", `$3`, '1', '2'",
1047 replace_params(params, stmt, query).unwrap()
1048 );
1049
1050 let query = "SELECT /* ? */ ? -- ?\n, ?".to_string();
1051 let stmt = statement_with_transformed_placeholders(&query);
1052 let params = vec!["'first'".to_string(), "'second'".to_string()];
1053
1054 assert_eq!(
1055 "SELECT /* ? */ 'first' -- ?\n, 'second'",
1056 replace_params(params, stmt, query).unwrap()
1057 );
1058
1059 let query = "SELECT '中文', ?".to_string();
1060 let stmt = statement_with_transformed_placeholders(&query);
1061 let params = vec!["'value'".to_string()];
1062
1063 assert_eq!(
1064 "SELECT '中文', 'value'",
1065 replace_params(params, stmt, query).unwrap()
1066 );
1067
1068 let query = "SELECT '中文',\n ?".to_string();
1069 let stmt = statement_with_transformed_placeholders(&query);
1070 let params = vec!["'value'".to_string()];
1071
1072 assert_eq!(
1073 "SELECT '中文',\n 'value'",
1074 replace_params(params, stmt, query).unwrap()
1075 );
1076
1077 let query = "SELECT 'x'\r\n, ?".to_string();
1078 let stmt = statement_with_transformed_placeholders(&query);
1079 let params = vec!["'crlf'".to_string()];
1080
1081 assert_eq!(
1082 "SELECT 'x'\r\n, 'crlf'",
1083 replace_params(params, stmt, query).unwrap()
1084 );
1085
1086 let query = "SELECT\t?".to_string();
1087 let stmt = statement_with_transformed_placeholders(&query);
1088 let params = vec!["NULL".to_string()];
1089
1090 assert_eq!("SELECT\tNULL", replace_params(params, stmt, query).unwrap());
1091
1092 let query = "SELECT CAST(? AS INT64), ? + (SELECT ?)".to_string();
1093 let stmt = statement_with_transformed_placeholders(&query);
1094 let params = vec!["1".to_string(), "2".to_string(), "3".to_string()];
1095
1096 assert_eq!(
1097 "SELECT CAST(1 AS INT64), 2 + (SELECT 3)",
1098 replace_params(params, stmt, query).unwrap()
1099 );
1100
1101 let query = "SET time_zone = ?".to_string();
1102 let stmt = statement_with_transformed_placeholders(&query);
1103 let params = vec!["'UTC'".to_string()];
1104
1105 assert_eq!(
1106 "SET time_zone = 'UTC'",
1107 replace_params(params, stmt, query).unwrap()
1108 );
1109 }
1110
1111 #[tokio::test]
1112 async fn test_prepare_federated_query() {
1113 let mut shim = create_shim();
1114 let query_ctx = QueryContext::arc();
1115 let stmt_key = "test_federated".to_string();
1116
1117 let (params, columns) = shim
1118 .do_prepare(
1119 "SELECT @@version_comment",
1120 query_ctx.clone(),
1121 stmt_key.clone(),
1122 )
1123 .await
1124 .unwrap();
1125
1126 assert!(params.is_empty());
1127 assert!(columns.is_empty());
1128
1129 let plan = shim.plan(&stmt_key).unwrap();
1130 assert!(matches!(plan, SqlPlan::Shortcut(q) if q == "SELECT @@version_comment"));
1131 }
1132
1133 #[tokio::test]
1134 async fn test_execute_federated_shortcut() {
1135 let mut shim = create_shim();
1136 let query_ctx = QueryContext::arc();
1137 let stmt_key = "test_federated_exec".to_string();
1138
1139 shim.do_prepare(
1140 "SELECT @@version_comment",
1141 query_ctx.clone(),
1142 stmt_key.clone(),
1143 )
1144 .await
1145 .unwrap();
1146
1147 let outputs = shim
1148 .do_execute(query_ctx.clone(), stmt_key, Params::CliParams(vec![]))
1149 .await
1150 .unwrap();
1151
1152 assert_eq!(outputs.len(), 1);
1153 let output = outputs.into_iter().next().unwrap().unwrap();
1154 let pretty = output.data.pretty_print().await;
1155 assert!(pretty.contains("GreptimeDB"));
1156 }
1157
1158 #[tokio::test]
1159 async fn test_prepare_non_federated_query_not_shortcut() {
1160 let mut shim = create_shim();
1161 let query_ctx = QueryContext::arc();
1162 let stmt_key = "test_non_federated".to_string();
1163
1164 let result = shim
1165 .do_prepare("SET NAMES utf8", query_ctx.clone(), stmt_key.clone())
1166 .await;
1167
1168 assert!(result.is_ok());
1169 let plan = shim.plan(&stmt_key).unwrap();
1170 assert!(matches!(plan, SqlPlan::Shortcut(_)));
1171 }
1172
1173 #[tokio::test]
1174 async fn test_execute_set_shortcut() {
1175 let mut shim = create_shim();
1176 let query_ctx = QueryContext::arc();
1177 let stmt_key = "test_set_shortcut".to_string();
1178
1179 shim.do_prepare("SET NAMES utf8", query_ctx.clone(), stmt_key.clone())
1180 .await
1181 .unwrap();
1182
1183 let outputs = shim
1184 .do_execute(query_ctx.clone(), stmt_key, Params::CliParams(vec![]))
1185 .await
1186 .unwrap();
1187
1188 assert_eq!(outputs.len(), 1);
1189 let output = outputs.into_iter().next().unwrap().unwrap();
1190 match output.data {
1191 common_query::OutputData::RecordBatches(batches) => {
1192 let total_rows: usize = batches.iter().map(|b| b.num_rows()).sum();
1193 assert_eq!(total_rows, 0);
1194 }
1195 other => panic!("Expected RecordBatches, got {:?}", other),
1196 }
1197 }
1198}