From 53ea4f624e33966b76ba43a227e0d6e1461cc17c Mon Sep 17 00:00:00 2001 From: Bo Lu Date: Mon, 9 Sep 2024 14:42:42 +1000 Subject: [PATCH] feat: add boolean test qual support --- supabase-wrappers/src/interface.rs | 6 +++++ supabase-wrappers/src/qual.rs | 32 +++++++++++++++++++++++++ wrappers/src/fdw/mssql_fdw/README.md | 1 + wrappers/src/fdw/mssql_fdw/mssql_fdw.rs | 30 +++++++++++++++++++++-- wrappers/src/fdw/mssql_fdw/tests.rs | 19 ++++++++++++--- 5 files changed, 83 insertions(+), 5 deletions(-) diff --git a/supabase-wrappers/src/interface.rs b/supabase-wrappers/src/interface.rs index 420001237..f9745bd12 100644 --- a/supabase-wrappers/src/interface.rs +++ b/supabase-wrappers/src/interface.rs @@ -234,6 +234,7 @@ impl CellFormatter for DefaultFormatter { format!("{}", cell) } } + /// A data row in a table /// /// The row contains a column name list and cell list with same number of @@ -353,6 +354,11 @@ pub struct Param { /// ``` /// /// ```sql +/// where bool_col is true +/// -- [Qual { field: "bool_col", operator: "is", value: Cell(Bool(true)), use_or: false }] +/// ``` +/// +/// ```sql /// where id > 1 and col = 'foo'; /// -- [ /// -- Qual { field: "id", operator: ">", value: Cell(I32(1)), use_or: false }, diff --git a/supabase-wrappers/src/qual.rs b/supabase-wrappers/src/qual.rs index b84fa4be7..2c3115ef7 100644 --- a/supabase-wrappers/src/qual.rs +++ b/supabase-wrappers/src/qual.rs @@ -303,6 +303,36 @@ pub(crate) unsafe fn extract_from_bool_expr( Some(qual) } +pub(crate) unsafe fn extract_from_boolean_test( + baserel_id: pg_sys::Oid, + expr: *mut pg_sys::BooleanTest, +) -> Option { + let var = (*expr).arg as *mut pg_sys::Var; + if !is_a(var as _, pg_sys::NodeTag::T_Var) || (*var).varattno < 1 { + return None; + } + + let field = pg_sys::get_attname(baserel_id, (*var).varattno, false); + + let (opname, value) = match (*expr).booltesttype { + pg_sys::BoolTestType_IS_TRUE => ("is".to_string(), true), + pg_sys::BoolTestType_IS_FALSE => ("is".to_string(), false), + pg_sys::BoolTestType_IS_NOT_TRUE => ("is not".to_string(), true), + pg_sys::BoolTestType_IS_NOT_FALSE => ("is not".to_string(), false), + _ => return None, + }; + + let qual = Qual { + field: CStr::from_ptr(field).to_str().unwrap().to_string(), + operator: opname, + value: Value::Cell(Cell::Bool(value)), + use_or: false, + param: None, + }; + + Some(qual) +} + pub(crate) unsafe fn extract_quals( root: *mut pg_sys::PlannerInfo, baserel: *mut pg_sys::RelOptInfo, @@ -323,6 +353,8 @@ pub(crate) unsafe fn extract_quals( extract_from_var(root, baserel_id, (*baserel).relids, expr as _) } else if is_a(expr, pg_sys::NodeTag::T_BoolExpr) { extract_from_bool_expr(root, baserel_id, (*baserel).relids, expr as _) + } else if is_a(expr, pg_sys::NodeTag::T_BooleanTest) { + extract_from_boolean_test(baserel_id, expr as _) } else { if let Some(stm) = pgrx::nodes::node_to_string(expr) { report_warning(&format!("unsupported qual: {}", stm)); diff --git a/wrappers/src/fdw/mssql_fdw/README.md b/wrappers/src/fdw/mssql_fdw/README.md index 5ed3159f7..f411e58d4 100644 --- a/wrappers/src/fdw/mssql_fdw/README.md +++ b/wrappers/src/fdw/mssql_fdw/README.md @@ -10,5 +10,6 @@ This is a foreign data wrapper for [Microsoft SQL Server](https://www.microsoft. | Version | Date | Notes | | ------- | ---------- | ---------------------------------------------------- | +| 0.1.1 | 2024-09-09 | Add boolean test qual support | | 0.1.0 | 2023-12-27 | Initial version | diff --git a/wrappers/src/fdw/mssql_fdw/mssql_fdw.rs b/wrappers/src/fdw/mssql_fdw/mssql_fdw.rs index eb1c3595c..d8f5ecd78 100644 --- a/wrappers/src/fdw/mssql_fdw/mssql_fdw.rs +++ b/wrappers/src/fdw/mssql_fdw/mssql_fdw.rs @@ -80,8 +80,20 @@ fn field_to_cell(src_row: &tiberius::Row, tgt_col: &Column) -> MssqlFdwResult String { + match cell { + // format boolean type to 0 or 1 + Cell::Bool(v) => format!("{}", *v as u8), + _ => format!("{}", cell), + } + } +} + #[wrappers_fdw( - version = "0.1.0", + version = "0.1.1", author = "Supabase", website = "https://github.com/supabase/wrappers/tree/main/wrappers/src/fdw/mssql_fdw", error_type = "MssqlFdwError" @@ -120,7 +132,21 @@ impl MssqlFdw { if !quals.is_empty() { let cond = quals .iter() - .map(|q| q.deparse()) + .map(|q| { + let oper = q.operator.as_str(); + let mut fmt = MssqlCellFormatter {}; + if let Value::Cell(cell) = &q.value { + // deparse boolean test qual, e.g. "bool_col is true" => "bool_col = 1" + if let Cell::Bool(_) = cell { + if oper == "is" { + return format!("{} = {}", q.field, fmt.fmt_cell(cell)); + } else if oper == "is not" { + return format!("{} <> {}", q.field, fmt.fmt_cell(cell)); + } + } + } + q.deparse_with_fmt(&mut fmt) + }) .collect::>() .join(" and "); diff --git a/wrappers/src/fdw/mssql_fdw/tests.rs b/wrappers/src/fdw/mssql_fdw/tests.rs index dd73524e4..4caa953c3 100644 --- a/wrappers/src/fdw/mssql_fdw/tests.rs +++ b/wrappers/src/fdw/mssql_fdw/tests.rs @@ -34,6 +34,7 @@ mod tests { r#"CREATE TABLE users ( id bigint, name varchar(30), + is_admin bit, dt datetime2 )"#, &[], @@ -46,9 +47,9 @@ mod tests { client .execute( r#" - INSERT INTO users(id, name, dt) VALUES (42, 'foo', '2023-12-28'); - INSERT INTO users(id, name, dt) VALUES (43, 'bar', '2023-12-27'); - INSERT INTO users(id, name, dt) VALUES (44, 'baz', '2023-12-26'); + INSERT INTO users(id, name, is_admin, dt) VALUES (42, 'foo', 0, '2023-12-28'); + INSERT INTO users(id, name, is_admin, dt) VALUES (43, 'bar', 1, '2023-12-27'); + INSERT INTO users(id, name, is_admin, dt) VALUES (44, 'baz', 0, '2023-12-26'); "#, &[], ) @@ -79,6 +80,7 @@ mod tests { CREATE FOREIGN TABLE mssql_users ( id bigint, name text, + is_admin boolean, dt timestamp ) SERVER mssql_server @@ -163,6 +165,17 @@ mod tests { .filter_map(|r| r.get_by_name::<&str, _>("name").unwrap()) .collect::>(); assert_eq!(results, vec!["foo", "bar"]); + + let results = c + .select( + "SELECT name FROM mssql_users WHERE is_admin is true", + None, + None, + ) + .unwrap() + .filter_map(|r| r.get_by_name::<&str, _>("name").unwrap()) + .collect::>(); + assert_eq!(results, vec!["bar"]); }); let result = std::panic::catch_unwind(|| {