From 91a28d16311fa803ff5f2ae7019f49be2df09dc5 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Fri, 17 Jul 2026 23:54:40 +0200 Subject: [PATCH] Remove unused SQLx macro facade Depend directly on sqlx-core-oldapi and use its module-level runtime APIs. SQLPage uses no SQLx query or derive macros, so this removes sqlx-oldapi and sqlx-macros-oldapi from the resolved graph. macOS arm64 cargo build --release: 43,037,040 -> 43,020,288 bytes (-16,752 bytes, -0.04%). --- Cargo.lock | 32 +----------- Cargo.toml | 2 +- src/filesystem.rs | 7 +-- src/telemetry_metrics.rs | 2 +- src/webserver/database/connect.rs | 9 ++-- src/webserver/database/csv_import.rs | 12 ++--- src/webserver/database/execute_queries.rs | 22 +++++--- src/webserver/database/mod.rs | 2 +- src/webserver/database/sql_to_json.rs | 62 +++++++++++++---------- src/webserver/error.rs | 4 +- tests/core/mod.rs | 6 +-- 11 files changed, 72 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 372d3217..bb27a151 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4624,7 +4624,7 @@ dependencies = [ "serde_json", "sha2 0.11.0", "sqlparser", - "sqlx-oldapi", + "sqlx-core-oldapi", "tokio", "tokio-stream", "tokio-util", @@ -4719,36 +4719,6 @@ dependencies = [ "whoami", ] -[[package]] -name = "sqlx-macros-oldapi" -version = "0.6.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adbf4ebc08c19991fa51993f471e572930c4dec146d3dc915a8e54db91d624c6" -dependencies = [ - "dotenvy", - "either", - "heck", - "once_cell", - "proc-macro2", - "quote", - "serde_json", - "sha2 0.11.0", - "sqlx-core-oldapi", - "sqlx-rt-oldapi", - "syn", - "url", -] - -[[package]] -name = "sqlx-oldapi" -version = "0.6.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9ba4d352504ee1a0a76eb052879d68ba63536c738da5062026ac0d3dc724c7" -dependencies = [ - "sqlx-core-oldapi", - "sqlx-macros-oldapi", -] - [[package]] name = "sqlx-rt-oldapi" version = "0.6.56" diff --git a/Cargo.toml b/Cargo.toml index ac5adb17..14973e0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ panic = "abort" codegen-units = 2 [dependencies] -sqlx = { package = "sqlx-oldapi", version = "0.6.56", default-features = false, features = [ +sqlx = { package = "sqlx-core-oldapi", version = "0.6.56", default-features = false, features = [ "any", "runtime-tokio-rustls", "migrate", diff --git a/src/filesystem.rs b/src/filesystem.rs index 4530cc98..42284dc4 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -5,8 +5,9 @@ use crate::{AppState, TEMPLATES_DIR}; use anyhow::Context; use chrono::{DateTime, Utc}; use sqlx::any::{AnyStatement, AnyTypeInfo}; +use sqlx::executor::Executor; use sqlx::postgres::types::PgTimeTz; -use sqlx::{Executor, Postgres, Statement, Type}; +use sqlx::{postgres::Postgres, statement::Statement, types::Type}; use std::fmt::Write; use std::io::ErrorKind; use std::path::{Component, Path, PathBuf}; @@ -429,7 +430,7 @@ impl DbFsQueries { #[actix_web::test] async fn test_sql_file_read_utf8() -> anyhow::Result<()> { use crate::app_config; - use sqlx::Executor; + use sqlx::executor::Executor; let config = app_config::tests::test_config(); let state = AppState::init(&config).await?; @@ -463,7 +464,7 @@ async fn test_sql_file_read_utf8() -> anyhow::Result<()> { make_placeholder(dbms, 1), make_placeholder(dbms, 2) ); - sqlx::query(&insert_sql) + sqlx::query::query(&insert_sql) .bind("unit test file.txt") .bind("Héllö world! 😀".as_bytes()) .execute(conn) diff --git a/src/telemetry_metrics.rs b/src/telemetry_metrics.rs index 89974438..9b2bf6e5 100644 --- a/src/telemetry_metrics.rs +++ b/src/telemetry_metrics.rs @@ -2,7 +2,7 @@ use opentelemetry::global; use opentelemetry::metrics::{Histogram, ObservableGauge}; use opentelemetry_semantic_conventions::attribute as otel; use opentelemetry_semantic_conventions::metric as otel_metric; -use sqlx::AnyPool; +use sqlx::any::AnyPool; pub struct TelemetryMetrics { pub http_request_duration: Histogram, diff --git a/src/webserver/database/connect.rs b/src/webserver/database/connect.rs index 3433e81e..4c717768 100644 --- a/src/webserver/database/connect.rs +++ b/src/webserver/database/connect.rs @@ -8,9 +8,10 @@ use crate::{ }; use anyhow::Context; use futures_util::future::BoxFuture; +use sqlx::connection::{ConnectOptions, Connection}; +use sqlx::executor::Executor; use sqlx::odbc::OdbcConnectOptions; use sqlx::{ - ConnectOptions, Connection, Executor, any::{Any, AnyConnectOptions, AnyConnection, AnyKind}, pool::PoolOptions, sqlite::{Function, SqliteConnectOptions, SqliteFunctionCtx}, @@ -146,11 +147,11 @@ fn add_on_return_to_pool(config: &AppConfig, pool_options: PoolOptions) -> } fn on_return_to_pool( - conn: &mut sqlx::AnyConnection, + conn: &mut sqlx::any::AnyConnection, meta: sqlx::pool::PoolConnectionMetadata, sql: std::sync::Arc, -) -> BoxFuture<'_, Result> { - use sqlx::Row; +) -> BoxFuture<'_, Result> { + use sqlx::row::Row; Box::pin(async move { log::trace!("Running the custom SQL connection cleanup handler. {meta:?}"); let query_result = conn.fetch_optional(sql.as_str()).await?; diff --git a/src/webserver/database/csv_import.rs b/src/webserver/database/csv_import.rs index 94ac13df..c1c25e26 100644 --- a/src/webserver/database/csv_import.rs +++ b/src/webserver/database/csv_import.rs @@ -5,10 +5,10 @@ use futures_util::StreamExt; use sqlparser::ast::{ CopyLegacyCsvOption, CopyLegacyOption, CopyOption, CopySource, CopyTarget, Statement, }; -use sqlx::{ - AnyConnection, Arguments, Executor, PgConnection, - any::{AnyArguments, AnyConnectionKind, AnyKind}, -}; +use sqlx::any::{AnyArguments, AnyConnection, AnyConnectionKind, AnyKind}; +use sqlx::arguments::Arguments; +use sqlx::executor::Executor; +use sqlx::postgres::PgConnection; use tokio::io::AsyncRead; use crate::webserver::http_request_info::RequestInfo; @@ -337,7 +337,7 @@ fn test_make_statement() { #[actix_web::test] async fn test_end_to_end() { - use sqlx::ConnectOptions; + use sqlx::connection::ConnectOptions; let mut copy_stmt = sqlparser::parser::Parser::parse_sql( &sqlparser::dialect::GenericDialect {}, @@ -376,7 +376,7 @@ async fn test_end_to_end() { run_csv_import_insert(&mut conn, &csv_import, file) .await .unwrap(); - let rows: Vec<(String, String)> = sqlx::query_as("SELECT * FROM my_table") + let rows: Vec<(String, String)> = sqlx::query_as::query_as("SELECT * FROM my_table") .fetch_all(&mut conn) .await .unwrap(); diff --git a/src/webserver/database/execute_queries.rs b/src/webserver/database/execute_queries.rs index 8ba29e3d..0212c7c6 100644 --- a/src/webserver/database/execute_queries.rs +++ b/src/webserver/database/execute_queries.rs @@ -21,13 +21,19 @@ use crate::webserver::http_request_info::ExecutionContext; use crate::webserver::single_or_vec::SingleOrVec; use super::{Database, DbItem, ScalarSubqueryBehavior, error_highlighting::display_db_error}; -use sqlx::any::{AnyArguments, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo}; -use sqlx::pool::PoolConnection; -use sqlx::{ - Any, AnyConnection, Arguments, Column, Either, Executor, Row as _, Statement, ValueRef, +use sqlx::Either; +use sqlx::any::{ + Any, AnyArguments, AnyConnection, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo, }; +use sqlx::arguments::Arguments; +use sqlx::column::Column; +use sqlx::executor::{Execute, Executor}; +use sqlx::pool::PoolConnection; +use sqlx::row::Row as _; +use sqlx::statement::Statement; +use sqlx::value::ValueRef; -pub type DbConn = Option>; +pub type DbConn = Option>; /// One database result together with private values reserved for computed /// columns and therefore omitted from the user-visible row. @@ -538,7 +544,7 @@ async fn take_connection<'a>( db: &'a Database, conn: &'a mut DbConn, request: &ExecutionContext, -) -> anyhow::Result<&'a mut PoolConnection> { +) -> anyhow::Result<&'a mut PoolConnection> { if let Some(c) = conn { return Ok(c); } @@ -609,7 +615,7 @@ fn parse_single_sql_result( source_file: &Path, query: &DatabaseQuery, source_span: SourceSpan, - res: sqlx::Result>, + res: sqlx::error::Result>, ) -> QueryResult { match res { Ok(Either::Right(r)) => { @@ -790,7 +796,7 @@ pub struct BoundQuery<'a> { param_values: Vec>, } -impl<'q> sqlx::Execute<'q, Any> for BoundQuery<'q> { +impl<'q> Execute<'q, Any> for BoundQuery<'q> { fn sql(&self) -> &'q str { self.sql } diff --git a/src/webserver/database/mod.rs b/src/webserver/database/mod.rs index 000e1647..b8c2d615 100644 --- a/src/webserver/database/mod.rs +++ b/src/webserver/database/mod.rs @@ -125,7 +125,7 @@ impl From for SupportedDatabase { } pub struct Database { - pub connection: sqlx::AnyPool, + pub connection: sqlx::any::AnyPool, pub info: DbInfo, } diff --git a/src/webserver/database/sql_to_json.rs b/src/webserver/database/sql_to_json.rs index 72c0f40e..5d34fa18 100644 --- a/src/webserver/database/sql_to_json.rs +++ b/src/webserver/database/sql_to_json.rs @@ -21,10 +21,14 @@ use bigdecimal::BigDecimal; use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime}; use serde_json::{self, Map, Value}; use sqlx::any::{AnyColumn, AnyRow, AnyTypeInfo, AnyTypeInfoKind}; +use sqlx::column::Column; +use sqlx::decode::Decode; use sqlx::postgres::PgValueRef; use sqlx::postgres::types::PgRange; -use sqlx::{Column, Row, TypeInfo, ValueRef}; -use sqlx::{Decode, Type}; +use sqlx::row::Row; +use sqlx::type_info::TypeInfo; +use sqlx::types::Type; +use sqlx::value::ValueRef; #[cfg(test)] pub fn row_to_json(row: &AnyRow) -> Value { @@ -118,13 +122,13 @@ fn decode_pg_range<'r, T>(raw_value: sqlx::any::AnyValueRef<'r>) -> Value where T: std::fmt::Display + Type - + for<'a> sqlx::Decode<'a, sqlx::postgres::Postgres>, + + for<'a> sqlx::decode::Decode<'a, sqlx::postgres::Postgres>, { let Ok(pg_val): Result, _> = raw_value.try_into() else { log::error!("Only postgres range values are supported"); return Value::Null; }; - match as sqlx::Decode<'r, sqlx::postgres::Postgres>>::decode(pg_val) { + match as sqlx::decode::Decode<'r, sqlx::postgres::Postgres>>::decode(pg_val) { Ok(pg_range) => pg_range.to_string().into(), Err(e) => { log::error!("Failed to decode postgres range value: {e}"); @@ -205,7 +209,7 @@ mod tests { use crate::app_config::tests::test_database_url; use super::*; - use sqlx::Connection; + use sqlx::connection::Connection; fn setup_logging() { crate::telemetry::init_test_logging(); @@ -224,10 +228,10 @@ mod tests { #[actix_web::test] async fn test_row_to_json() -> anyhow::Result<()> { - use sqlx::Connection; + use sqlx::connection::Connection; let db_url = test_database_url(); - let mut c = sqlx::AnyConnection::connect(&db_url).await?; - let row = sqlx::query( + let mut c = sqlx::any::AnyConnection::connect(&db_url).await?; + let row = sqlx::query::query( "SELECT 123.456 as one_value, 1 as two_values, @@ -253,8 +257,8 @@ mod tests { #[actix_web::test] async fn private_inputs_are_split_from_the_trailing_suffix() -> anyhow::Result<()> { let db_url = test_database_url(); - let mut connection = sqlx::AnyConnection::connect(&db_url).await?; - let row = sqlx::query( + let mut connection = sqlx::any::AnyConnection::connect(&db_url).await?; + let row = sqlx::query::query( "SELECT 'public' AS \"__sqlpage_input_0\", 'private' AS \"__sqlpage_input_0\"", ) .fetch_one(&mut connection) @@ -273,8 +277,8 @@ mod tests { let Some(db_url) = db_specific_test("postgres") else { return Ok(()); }; - let mut c = sqlx::AnyConnection::connect(&db_url).await?; - let row = sqlx::query( + let mut c = sqlx::any::AnyConnection::connect(&db_url).await?; + let row = sqlx::query::query( "SELECT 42::INT2 as small_int, 42::INT4 as integer, @@ -350,8 +354,8 @@ mod tests { let Some(db_url) = db_specific_test("postgres") else { return Ok(()); }; - let mut c = sqlx::AnyConnection::connect(&db_url).await?; - let row = sqlx::query( + let mut c = sqlx::any::AnyConnection::connect(&db_url).await?; + let row = sqlx::query::query( "SELECT '2024-03-14'::DATE as date, '13:14:15'::TIME as time, @@ -388,8 +392,8 @@ mod tests { let Some(db_url) = db_specific_test("postgres") else { return Ok(()); }; - let mut c = sqlx::AnyConnection::connect(&db_url).await?; - let row = sqlx::query( + let mut c = sqlx::any::AnyConnection::connect(&db_url).await?; + let row = sqlx::query::query( "SELECT '[1,5)'::INT4RANGE as int4range, '[2024-11-12 01:02:03,2024-11-12 23:00:00)'::TSRANGE as tsrange, @@ -419,9 +423,9 @@ mod tests { let Some(db_url) = db_url else { return Ok(()); }; - let mut c = sqlx::AnyConnection::connect(&db_url).await?; + let mut c = sqlx::any::AnyConnection::connect(&db_url).await?; - sqlx::query( + sqlx::query::query( "CREATE TEMPORARY TABLE _sqlp_t ( tiny_int TINYINT, small_int SMALLINT, @@ -475,7 +479,7 @@ mod tests { .execute(&mut c) .await?; - let row = sqlx::query("SELECT * FROM _sqlp_t") + let row = sqlx::query::query("SELECT * FROM _sqlp_t") .fetch_one(&mut c) .await?; @@ -508,7 +512,9 @@ mod tests { }), ); - sqlx::query("DROP TABLE _sqlp_t").execute(&mut c).await?; + sqlx::query::query("DROP TABLE _sqlp_t") + .execute(&mut c) + .await?; Ok(()) } @@ -518,8 +524,8 @@ mod tests { let Some(db_url) = db_specific_test("sqlite") else { return Ok(()); }; - let mut c = sqlx::AnyConnection::connect(&db_url).await?; - let row = sqlx::query( + let mut c = sqlx::any::AnyConnection::connect(&db_url).await?; + let row = sqlx::query::query( "SELECT 42 as integer, 42.25 as real, @@ -546,8 +552,8 @@ mod tests { let Some(db_url) = db_specific_test("mssql") else { return Ok(()); }; - let mut c = sqlx::AnyConnection::connect(&db_url).await?; - let row = sqlx::query( + let mut c = sqlx::any::AnyConnection::connect(&db_url).await?; + let row = sqlx::query::query( "SELECT CAST(1 AS BIT) as true_bit, CAST(0 AS BIT) as false_bit, @@ -639,10 +645,10 @@ mod tests { #[actix_web::test] async fn test_canonical_col_name_variations() -> anyhow::Result<()> { let db_url = test_database_url(); - let mut c = sqlx::AnyConnection::connect(&db_url).await?; + let mut c = sqlx::any::AnyConnection::connect(&db_url).await?; // Test various column name formats to ensure canonical_col_name works correctly - let row = sqlx::query( + let row = sqlx::query::query( r#"SELECT 42 as "UPPERCASE_COL", 42 as "lowercase_col", @@ -695,11 +701,11 @@ mod tests { #[actix_web::test] async fn test_row_to_json_edge_cases() -> anyhow::Result<()> { let db_url = test_database_url(); - let mut c = sqlx::AnyConnection::connect(&db_url).await?; + let mut c = sqlx::any::AnyConnection::connect(&db_url).await?; let dbms_name = c.dbms_name().await.expect("retrieve db name"); // Test edge cases for row_to_json - let row = sqlx::query( + let row = sqlx::query::query( "SELECT NULL as null_col, '' as empty_string, diff --git a/src/webserver/error.rs b/src/webserver/error.rs index 817d7083..f22ec1b9 100644 --- a/src/webserver/error.rs +++ b/src/webserver/error.rs @@ -183,7 +183,7 @@ pub(super) fn anyhow_err_to_actix_resp(e: &anyhow::Error, state: &AppState) -> H "Basic realm=\"Authentication required\", charset=\"UTF-8\"", )); } - } else if let Some(sqlx::Error::PoolTimedOut) = e.downcast_ref() { + } else if let Some(sqlx::error::Error::PoolTimedOut) = e.downcast_ref() { use rand::RngExt; resp.status(StatusCode::TOO_MANY_REQUESTS).insert_header(( header::RETRY_AFTER, @@ -211,7 +211,7 @@ pub(super) fn anyhow_err_to_actix_resp(e: &anyhow::Error, state: &AppState) -> H fn anyhow_error_status(e: &anyhow::Error) -> Option { if let Some(&ErrorWithStatus { status }) = e.downcast_ref() { Some(status) - } else if let Some(sqlx::Error::PoolTimedOut) = e.downcast_ref() { + } else if let Some(sqlx::error::Error::PoolTimedOut) = e.downcast_ref() { Some(StatusCode::TOO_MANY_REQUESTS) } else { None diff --git a/tests/core/mod.rs b/tests/core/mod.rs index 4f63ae14..395df28f 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -3,7 +3,7 @@ use sqlpage::{ AppState, webserver::{self, make_placeholder}, }; -use sqlx::Executor as _; +use sqlx::executor::Executor as _; use crate::common::{make_app_data_from_config, req_path, req_path_with_app_data, test_config}; @@ -79,7 +79,7 @@ async fn test_routing_with_db_fs() { "INSERT INTO sqlpage_files(path, contents) VALUES ('on_db.sql', {})", make_placeholder(state.db.info.kind, 1) ); - sqlx::query(&insert_sql) + sqlx::query::query(&insert_sql) .bind("select ''text'' as component, ''Hi from db !'' AS contents;".as_bytes()) .execute(&state.db.connection) .await @@ -125,7 +125,7 @@ async fn test_non_unicode_static_path_returns_bad_request_with_db_fs() { make_placeholder(state.db.info.kind, 1), make_placeholder(state.db.info.kind, 2) ); - sqlx::query(&insert_sql) + sqlx::query::query(&insert_sql) .bind(expected_db_path) .bind("file from db fs".as_bytes()) .execute(&mut *conn)