From 98ee1575da6a0d5754b4c224c067ce4382889cad Mon Sep 17 00:00:00 2001 From: Evgeniy Terekhin Date: Tue, 8 Nov 2022 16:39:55 +0200 Subject: [PATCH] add support for some sqlite specific binary operators --- src/backend/sqlite/query.rs | 17 +++++++++++++ src/extension/mod.rs | 4 +++ src/extension/sqlite/mod.rs | 18 +++++++++++++ src/types.rs | 4 +++ tests/sqlite/query.rs | 50 +++++++++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 src/extension/sqlite/mod.rs diff --git a/src/backend/sqlite/query.rs b/src/backend/sqlite/query.rs index 75b122b67..6cfaab824 100644 --- a/src/backend/sqlite/query.rs +++ b/src/backend/sqlite/query.rs @@ -1,4 +1,5 @@ use super::*; +use crate::extension::sqlite::SqliteBinOper; impl QueryBuilder for SqliteQueryBuilder { fn prepare_select_lock(&self, _select_lock: &LockClause, _sql: &mut dyn SqlWriter) { @@ -19,6 +20,22 @@ impl QueryBuilder for SqliteQueryBuilder { .unwrap(); } + fn prepare_bin_oper(&self, bin_oper: &BinOper, sql: &mut dyn SqlWriter) { + match bin_oper { + BinOper::SqliteOperator(bin_oper) => write!( + sql, + "{}", + match bin_oper { + SqliteBinOper::Match => "MATCH", + SqliteBinOper::GetJsonField => "->", + SqliteBinOper::CastJsonField => "->>", + } + ) + .unwrap(), + _ => self.prepare_bin_oper_common(bin_oper, sql), + } + } + fn prepare_query_statement(&self, query: &SubQueryStatement, sql: &mut dyn SqlWriter) { query.prepare_statement(self, sql); } diff --git a/src/extension/mod.rs b/src/extension/mod.rs index f9e697ab9..2767d1f3a 100644 --- a/src/extension/mod.rs +++ b/src/extension/mod.rs @@ -3,3 +3,7 @@ #[cfg(feature = "backend-postgres")] #[cfg_attr(docsrs, doc(cfg(feature = "backend-postgres")))] pub mod postgres; + +#[cfg(feature = "backend-sqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "backend-sqlite")))] +pub mod sqlite; diff --git a/src/extension/sqlite/mod.rs b/src/extension/sqlite/mod.rs new file mode 100644 index 000000000..21d52552b --- /dev/null +++ b/src/extension/sqlite/mod.rs @@ -0,0 +1,18 @@ +use crate::types::BinOper; + +/// Sqlite-specific binary operator. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SqliteBinOper { + /// 'MATCH'. + Match, + /// '->'. Retreives JSON field as JSON value. + GetJsonField, + /// '->>'. Retreives JSON field and casts it to an appropriate SQL type. + CastJsonField, +} + +impl From for BinOper { + fn from(o: SqliteBinOper) -> Self { + Self::SqliteOperator(o) + } +} diff --git a/src/types.rs b/src/types.rs index 80901a681..e099228f6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -5,6 +5,8 @@ use std::fmt; #[cfg(feature = "backend-postgres")] use crate::extension::postgres::PgBinOper; +#[cfg(feature = "backend-sqlite")] +use crate::extension::sqlite::SqliteBinOper; #[cfg(not(feature = "thread-safe"))] pub use std::rc::Rc as SeaRc; #[cfg(feature = "thread-safe")] @@ -137,6 +139,8 @@ pub enum BinOper { Escape, #[cfg(feature = "backend-postgres")] PgOperator(PgBinOper), + #[cfg(feature = "backend-sqlite")] + SqliteOperator(SqliteBinOper), } /// Logical chain operator diff --git a/tests/sqlite/query.rs b/tests/sqlite/query.rs index f953e02ce..a150abef7 100644 --- a/tests/sqlite/query.rs +++ b/tests/sqlite/query.rs @@ -1,5 +1,6 @@ use super::*; use pretty_assertions::assert_eq; +use sea_query::extension::sqlite::SqliteBinOper; #[test] fn select_1() { @@ -977,6 +978,55 @@ fn select_58() { ); } +#[test] +fn match_bin_oper() { + assert_eq!( + Query::select() + .column(Char::Character) + .from(Char::Table) + .and_where(Expr::col(Char::Character).binary(SqliteBinOper::Match, Expr::val("test"))) + .build(SqliteQueryBuilder), + ( + r#"SELECT "character" FROM "character" WHERE "character" MATCH ?"#.to_owned(), + Values(vec!["test".into()]) + ) + ); +} + +#[test] +fn get_json_field_bin_oper() { + assert_eq!( + Query::select() + .column(Char::Character) + .from(Char::Table) + .and_where( + Expr::col(Char::Character).binary(SqliteBinOper::GetJsonField, Expr::val("test")) + ) + .build(SqliteQueryBuilder), + ( + r#"SELECT "character" FROM "character" WHERE "character" -> ?"#.to_owned(), + Values(vec!["test".into()]) + ) + ); +} + +#[test] +fn cast_json_field_bin_oper() { + assert_eq!( + Query::select() + .column(Char::Character) + .from(Char::Table) + .and_where( + Expr::col(Char::Character).binary(SqliteBinOper::CastJsonField, Expr::val("test")) + ) + .build(SqliteQueryBuilder), + ( + r#"SELECT "character" FROM "character" WHERE "character" ->> ?"#.to_owned(), + Values(vec!["test".into()]) + ) + ); +} + #[test] #[allow(clippy::approx_constant)] fn insert_2() {