From 75ce343f9b1f26979f9b3a910724376f51658bbd Mon Sep 17 00:00:00 2001 From: pravic Date: Tue, 24 Sep 2024 10:23:40 +0300 Subject: [PATCH] feat(query): add `sql_display` to show SQL (#155) --- src/query.rs | 6 ++++++ src/sql/mod.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- tests/it/query.rs | 11 +++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/query.rs b/src/query.rs index 5a03d178..3189b2fe 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1,5 +1,6 @@ use hyper::{header::CONTENT_LENGTH, Method, Request}; use serde::Deserialize; +use std::fmt::Display; use url::Url; use crate::headers::with_request_headers; @@ -30,6 +31,11 @@ impl Query { } } + /// Display SQL query as string. + pub fn sql_display(&self) -> &impl Display { + &self.sql + } + /// Binds `value` to the next `?` in the query. /// /// The `value`, which must either implement [`Serialize`] or be an diff --git a/src/sql/mod.rs b/src/sql/mod.rs index edb42e71..6a8e07bb 100644 --- a/src/sql/mod.rs +++ b/src/sql/mod.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::fmt::{self, Display}; use crate::{ error::{Error, Result}, @@ -11,19 +11,38 @@ mod bind; pub(crate) mod escape; mod ser; -#[derive(Clone)] +#[derive(Debug, Clone)] pub(crate) enum SqlBuilder { InProgress(Vec), Failed(String), } -#[derive(Clone)] +#[derive(Debug, Clone)] pub(crate) enum Part { Arg, Fields, Text(String), } +/// Display SQL query as string. +impl fmt::Display for SqlBuilder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SqlBuilder::InProgress(parts) => { + for part in parts { + match part { + Part::Arg => write!(f, "?")?, + Part::Fields => write!(f, "?fields")?, + Part::Text(text) => write!(f, "{text}")?, + } + } + } + SqlBuilder::Failed(err) => write!(f, "{err}")?, + } + Ok(()) + } +} + impl SqlBuilder { pub(crate) fn new(template: &str) -> Self { let mut iter = template.split('?'); @@ -141,9 +160,29 @@ mod tests { #[test] fn bound_args() { let mut sql = SqlBuilder::new("SELECT ?fields FROM test WHERE a = ? AND b < ?"); + assert_eq!( + sql.to_string(), + "SELECT ?fields FROM test WHERE a = ? AND b < ?" + ); + sql.bind_arg("foo"); + assert_eq!( + sql.to_string(), + "SELECT ?fields FROM test WHERE a = 'foo' AND b < ?" + ); + sql.bind_arg(42); + assert_eq!( + sql.to_string(), + "SELECT ?fields FROM test WHERE a = 'foo' AND b < 42" + ); + sql.bind_fields::(); + assert_eq!( + sql.to_string(), + "SELECT `a`,`b` FROM test WHERE a = 'foo' AND b < 42" + ); + assert_eq!( sql.finish().unwrap(), r"SELECT `a`,`b` FROM test WHERE a = 'foo' AND b < 42" diff --git a/tests/it/query.rs b/tests/it/query.rs index aacab478..80e01588 100644 --- a/tests/it/query.rs +++ b/tests/it/query.rs @@ -214,3 +214,14 @@ async fn overrides_client_options() { // should override the client options assert_eq!(value, override_value); } + +#[tokio::test] +async fn prints_query() { + let client = prepare_database!(); + + let q = client.query("SELECT ?fields FROM test WHERE a = ? AND b < ?"); + assert_eq!( + format!("{}", q.sql_display()), + "SELECT ?fields FROM test WHERE a = ? AND b < ?" + ); +}