Skip to content

Commit e55384e

Browse files
committed
feat(orm): add an escape hatch for raw SELECT queries
1 parent f9b942c commit e55384e

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

cot/src/db.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,6 +1906,53 @@ impl Database {
19061906
}
19071907
}
19081908

1909+
/// Executes a raw SQL query and maps the results to a model type.
1910+
///
1911+
/// Unlike [`Database::raw`], this method returns the rows as model objects
1912+
/// by applying the model's [`Model::from_db`] mapping.
1913+
///
1914+
/// # Safety
1915+
///
1916+
/// This method executes the raw SQL string without any sanitization.
1917+
/// Callers are responsible for ensuring the query is safe.
1918+
///
1919+
/// # Errors
1920+
///
1921+
/// Returns an error if the query is invalid, if the model doesn't exist in
1922+
/// the database, or if the database connection is lost.
1923+
pub async fn raw_as<T: Model>(&self, query: &str) -> Result<Vec<T>> {
1924+
let rows = self.fetch_all_raw(query).await?;
1925+
rows.into_iter().map(T::from_db).collect::<Result<_>>()
1926+
}
1927+
1928+
async fn fetch_all_raw(&self, sql: &str) -> Result<Vec<Row>> {
1929+
let result = match &*self.inner {
1930+
#[cfg(feature = "sqlite")]
1931+
DatabaseImpl::Sqlite(inner) => inner
1932+
.fetch_all_raw(sql)
1933+
.await?
1934+
.into_iter()
1935+
.map(Row::Sqlite)
1936+
.collect(),
1937+
#[cfg(feature = "postgres")]
1938+
DatabaseImpl::Postgres(inner) => inner
1939+
.fetch_all_raw(sql)
1940+
.await?
1941+
.into_iter()
1942+
.map(Row::Postgres)
1943+
.collect(),
1944+
#[cfg(feature = "mysql")]
1945+
DatabaseImpl::MySql(inner) => inner
1946+
.fetch_all_raw(sql)
1947+
.await?
1948+
.into_iter()
1949+
.map(Row::MySql)
1950+
.collect(),
1951+
};
1952+
1953+
Ok(result)
1954+
}
1955+
19091956
async fn fetch_all<T>(&self, statement: &T) -> Result<Vec<Row>>
19101957
where
19111958
T: SqlxBinder + Send + Sync,

cot/src/db/sea_query_db.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,21 @@ macro_rules! impl_sea_query_db_backend {
8484
self.execute_sqlx(Self::sqlx_query_with(sql, values)).await
8585
}
8686

87+
pub(super) async fn fetch_all_raw(
88+
&self,
89+
sql: &str,
90+
) -> crate::db::Result<Vec<$row_name>> {
91+
tracing::debug!("Raw query: `{}`", sql);
92+
let result = sqlx::query(sqlx::AssertSqlSafe(sql))
93+
.fetch_all(&self.db_connection)
94+
.await
95+
.map_err(|err| crate::db::sea_query_db::map_sqlx_error(err))?
96+
.into_iter()
97+
.map($row_name::new)
98+
.collect();
99+
Ok(result)
100+
}
101+
87102
async fn execute_sqlx<'a, A>(
88103
&self,
89104
sqlx_statement: sqlx::query::Query<'a, $sqlx_db_ty, A>,

0 commit comments

Comments
 (0)