Skip to content

Commit 5ded901

Browse files
committed
Update sqlpage and sqlx dependencies, refactor database handling to use SupportedDatabase enum for improved database type management and placeholder generation.
1 parent f9e9be6 commit 5ded901

File tree

8 files changed

+162
-184
lines changed

8 files changed

+162
-184
lines changed

Cargo.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
[package]
22
name = "sqlpage"
3-
version = "0.37.1"
3+
version = "0.38.0-beta.1"
44
edition = "2021"
55
description = "Build data user interfaces entirely in SQL. A web server that takes .sql files and formats the query result using pre-made configurable professional-looking components."
66
keywords = ["web", "sql", "framework"]
77
license = "MIT"
88
homepage = "https://sql-page.com/"
99
repository = "https://github.com/sqlpage/SQLPage"
1010
documentation = "https://docs.rs/sqlpage"
11-
include = [
12-
"/src",
13-
"/README.md",
14-
"/build.rs",
15-
"/sqlpage",
16-
]
11+
include = ["/src", "/README.md", "/build.rs", "/sqlpage"]
1712

1813
[profile.superoptimized]
1914
inherits = "release"
@@ -23,7 +18,7 @@ panic = "abort"
2318
codegen-units = 2
2419

2520
[dependencies]
26-
sqlx = { package = "sqlx-oldapi", version = "0.6.49-beta", default-features = false, features = [
21+
sqlx = { package = "sqlx-oldapi", version = "0.6.49-beta.2", default-features = false, features = [
2722
"any",
2823
"runtime-tokio-rustls",
2924
"migrate",
@@ -50,7 +45,10 @@ anyhow = "1"
5045
serde = "1"
5146
serde_json = { version = "1.0.82", features = ["preserve_order", "raw_value"] }
5247
lambda-web = { version = "0.2.1", features = ["actix4"], optional = true }
53-
sqlparser = { version = "0.59.0", default-features = false, features = ["std", "visitor",] }
48+
sqlparser = { version = "0.59.0", default-features = false, features = [
49+
"std",
50+
"visitor",
51+
] }
5452
async-stream = "0.3"
5553
async-trait = "0.1.61"
5654
async-recursion = "1.0.0"

src/filesystem.rs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::webserver::{make_placeholder, Database};
33
use crate::{AppState, TEMPLATES_DIR};
44
use anyhow::Context;
55
use chrono::{DateTime, Utc};
6-
use sqlx::any::{AnyKind, AnyStatement, AnyTypeInfo};
6+
use sqlx::any::{AnyStatement, AnyTypeInfo};
7+
use crate::webserver::database::SupportedDatabase;
78
use sqlx::postgres::types::PgTimeTz;
89
use sqlx::{Postgres, Statement, Type};
910
use std::fmt::Write;
@@ -27,7 +28,7 @@ impl FileSystem {
2728
You can host sql files directly in your database by creating the following table: \n\
2829
{} \n\
2930
The error while trying to use the database file system is: {e:#}",
30-
DbFsQueries::get_create_table_sql(db.connection.any_kind())
31+
DbFsQueries::get_create_table_sql(db.database_type)
3132
);
3233
None
3334
}
@@ -206,32 +207,32 @@ pub struct DbFsQueries {
206207

207208
impl DbFsQueries {
208209
#[must_use]
209-
pub fn get_create_table_sql(db_kind: AnyKind) -> &'static str {
210-
match db_kind {
211-
AnyKind::Mssql => "CREATE TABLE sqlpage_files(path NVARCHAR(255) NOT NULL PRIMARY KEY, contents VARBINARY(MAX), last_modified DATETIME2(3) NOT NULL DEFAULT CURRENT_TIMESTAMP);",
212-
AnyKind::Postgres => "CREATE TABLE IF NOT EXISTS sqlpage_files(path VARCHAR(255) NOT NULL PRIMARY KEY, contents BYTEA, last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP);",
210+
pub fn get_create_table_sql(dbms: SupportedDatabase) -> &'static str {
211+
match dbms {
212+
SupportedDatabase::Mssql => "CREATE TABLE sqlpage_files(path NVARCHAR(255) NOT NULL PRIMARY KEY, contents VARBINARY(MAX), last_modified DATETIME2(3) NOT NULL DEFAULT CURRENT_TIMESTAMP);",
213+
SupportedDatabase::Postgres => "CREATE TABLE IF NOT EXISTS sqlpage_files(path VARCHAR(255) NOT NULL PRIMARY KEY, contents BYTEA, last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP);",
213214
_ => "CREATE TABLE IF NOT EXISTS sqlpage_files(path VARCHAR(255) NOT NULL PRIMARY KEY, contents BLOB, last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP);",
214215
}
215216
}
216217

217218
async fn init(db: &Database) -> anyhow::Result<Self> {
218219
log::debug!("Initializing database filesystem queries");
219-
let db_kind = db.connection.any_kind();
220+
let dbms = db.database_type;
220221
Ok(Self {
221-
was_modified: Self::make_was_modified_query(db, db_kind).await?,
222-
read_file: Self::make_read_file_query(db, db_kind).await?,
223-
exists: Self::make_exists_query(db, db_kind).await?,
222+
was_modified: Self::make_was_modified_query(db, dbms).await?,
223+
read_file: Self::make_read_file_query(db, dbms).await?,
224+
exists: Self::make_exists_query(db, dbms).await?,
224225
})
225226
}
226227

227228
async fn make_was_modified_query(
228229
db: &Database,
229-
db_kind: AnyKind,
230+
dbms: SupportedDatabase,
230231
) -> anyhow::Result<AnyStatement<'static>> {
231232
let was_modified_query = format!(
232233
"SELECT 1 from sqlpage_files WHERE last_modified >= {} AND path = {}",
233-
make_placeholder(db_kind, 1),
234-
make_placeholder(db_kind, 2)
234+
make_placeholder(dbms, 1),
235+
make_placeholder(dbms, 2)
235236
);
236237
let param_types: &[AnyTypeInfo; 2] = &[
237238
PgTimeTz::type_info().into(),
@@ -243,11 +244,11 @@ impl DbFsQueries {
243244

244245
async fn make_read_file_query(
245246
db: &Database,
246-
db_kind: AnyKind,
247+
dbms: SupportedDatabase,
247248
) -> anyhow::Result<AnyStatement<'static>> {
248249
let read_file_query = format!(
249250
"SELECT contents from sqlpage_files WHERE path = {}",
250-
make_placeholder(db_kind, 1),
251+
make_placeholder(dbms, 1),
251252
);
252253
let param_types: &[AnyTypeInfo; 1] = &[<str as Type<Postgres>>::type_info().into()];
253254
log::debug!("Preparing the database filesystem read_file_query: {read_file_query}");
@@ -256,11 +257,11 @@ impl DbFsQueries {
256257

257258
async fn make_exists_query(
258259
db: &Database,
259-
db_kind: AnyKind,
260+
dbms: SupportedDatabase,
260261
) -> anyhow::Result<AnyStatement<'static>> {
261262
let exists_query = format!(
262263
"SELECT 1 from sqlpage_files WHERE path = {}",
263-
make_placeholder(db_kind, 1),
264+
make_placeholder(dbms, 1),
264265
);
265266
let param_types: &[AnyTypeInfo; 1] = &[<str as Type<Postgres>>::type_info().into()];
266267
db.prepare_with(&exists_query, param_types).await
@@ -356,11 +357,11 @@ async fn test_sql_file_read_utf8() -> anyhow::Result<()> {
356357
.execute(format!("DROP TABLE IF EXISTS sqlpage_files; {create_table_sql}").as_str())
357358
.await?;
358359

359-
let db_kind = state.db.connection.any_kind();
360+
let dbms = state.db.database_type;
360361
let insert_sql = format!(
361362
"INSERT INTO sqlpage_files(path, contents) VALUES ({}, {})",
362-
make_placeholder(db_kind, 1),
363-
make_placeholder(db_kind, 2)
363+
make_placeholder(dbms, 1),
364+
make_placeholder(dbms, 2)
364365
);
365366
sqlx::query(&insert_sql)
366367
.bind("unit test file.txt")

src/webserver/database/connect.rs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use std::{mem::take, time::Duration};
22

33
use super::Database;
4-
use crate::{app_config::AppConfig, ON_CONNECT_FILE, ON_RESET_FILE};
4+
use crate::{
5+
app_config::AppConfig, webserver::database::SupportedDatabase, ON_CONNECT_FILE, ON_RESET_FILE,
6+
};
57
use anyhow::Context;
68
use futures_util::future::BoxFuture;
79
use sqlx::{
8-
any::{Any, AnyConnectOptions, AnyKind},
10+
any::{Any, AnyConnectOptions},
911
pool::PoolOptions,
1012
sqlite::{Function, SqliteFunctionCtx},
11-
ConnectOptions, Executor,
13+
ConnectOptions, Connection, Executor,
1214
};
1315

1416
impl Database {
@@ -33,8 +35,11 @@ impl Database {
3335
set_custom_connect_options(&mut connect_options, config);
3436
log::debug!("Connecting to database: {database_url}");
3537
let mut retries = config.database_connection_retries;
36-
let connection = loop {
37-
match Self::create_pool_options(config, connect_options.kind())
38+
// Try to determine database type from connection string first
39+
let kind_str = format!("{:?}", connect_options.kind()).to_lowercase();
40+
let database_type = SupportedDatabase::from_dbms_name(&kind_str);
41+
let pool = loop {
42+
match Self::create_pool_options(config, database_type)
3843
.connect_with(connect_options.clone())
3944
.await
4045
{
@@ -50,44 +55,48 @@ impl Database {
5055
}
5156
}
5257
};
53-
log::debug!("Initialized database pool: {connection:#?}");
54-
Ok(Database { connection })
58+
let _dbms_name: String = pool.acquire().await?.dbms_name().await?;
59+
log::debug!("Initialized database pool: {pool:#?}");
60+
Ok(Database {
61+
connection: pool,
62+
database_type,
63+
})
5564
}
5665

57-
fn create_pool_options(config: &AppConfig, db_kind: AnyKind) -> PoolOptions<Any> {
66+
fn create_pool_options(config: &AppConfig, dbms: SupportedDatabase) -> PoolOptions<Any> {
5867
let mut pool_options = PoolOptions::new()
5968
.max_connections(if let Some(max) = config.max_database_pool_connections {
6069
max
6170
} else {
6271
// Different databases have a different number of max concurrent connections allowed by default
63-
match db_kind {
64-
AnyKind::Postgres | AnyKind::Odbc => 50, // Default to PostgreSQL-like limits for ODBC
65-
AnyKind::MySql => 75,
66-
AnyKind::Sqlite => {
72+
match dbms {
73+
SupportedDatabase::Postgres | SupportedDatabase::Generic => 50, // Default to PostgreSQL-like limits for Generic
74+
SupportedDatabase::MySql => 75,
75+
SupportedDatabase::Sqlite => {
6776
if config.database_url.contains(":memory:") {
6877
128
6978
} else {
7079
16
7180
}
7281
}
73-
AnyKind::Mssql => 100,
82+
SupportedDatabase::Mssql => 100,
7483
}
7584
})
7685
.idle_timeout(
7786
config
7887
.database_connection_idle_timeout_seconds
7988
.map(Duration::from_secs_f64)
80-
.or_else(|| match db_kind {
81-
AnyKind::Sqlite => None,
89+
.or_else(|| match dbms {
90+
SupportedDatabase::Sqlite => None,
8291
_ => Some(Duration::from_secs(30 * 60)),
8392
}),
8493
)
8594
.max_lifetime(
8695
config
8796
.database_connection_max_lifetime_seconds
8897
.map(Duration::from_secs_f64)
89-
.or_else(|| match db_kind {
90-
AnyKind::Sqlite => None,
98+
.or_else(|| match dbms {
99+
SupportedDatabase::Sqlite => None,
91100
_ => Some(Duration::from_secs(60 * 60)),
92101
}),
93102
)

src/webserver/database/csv_import.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ use sqlparser::ast::{
66
CopyLegacyCsvOption, CopyLegacyOption, CopyOption, CopySource, CopyTarget, Statement,
77
};
88
use sqlx::{
9-
any::{AnyArguments, AnyConnectionKind, AnyKind},
9+
any::{AnyArguments, AnyConnectionKind},
1010
AnyConnection, Arguments, Executor, PgConnection,
1111
};
12+
use crate::webserver::database::SupportedDatabase;
1213
use tokio::io::AsyncRead;
1314

1415
use crate::webserver::http_request_info::RequestInfo;
@@ -143,6 +144,7 @@ pub(super) fn extract_csv_copy_statement(stmt: &mut Statement) -> Option<CsvImpo
143144

144145
pub(super) async fn run_csv_import(
145146
db: &mut AnyConnection,
147+
dbms: SupportedDatabase,
146148
csv_import: &CsvImport,
147149
request: &RequestInfo,
148150
) -> anyhow::Result<()> {
@@ -173,7 +175,7 @@ pub(super) async fn run_csv_import(
173175
AnyConnectionKind::Postgres(pg_connection) => {
174176
run_csv_import_postgres(pg_connection, csv_import, buffered).await
175177
}
176-
_ => run_csv_import_insert(db, csv_import, buffered).await,
178+
_ => run_csv_import_insert(db, dbms, csv_import, buffered).await,
177179
}
178180
.with_context(|| {
179181
let table_name = &csv_import.table_name;
@@ -216,10 +218,11 @@ async fn run_csv_import_postgres(
216218

217219
async fn run_csv_import_insert(
218220
db: &mut AnyConnection,
221+
dbms: SupportedDatabase,
219222
csv_import: &CsvImport,
220223
file: impl AsyncRead + Unpin + Send,
221224
) -> anyhow::Result<()> {
222-
let insert_stmt = create_insert_stmt(db.kind(), csv_import);
225+
let insert_stmt = create_insert_stmt(dbms, csv_import);
223226
log::debug!("CSV data insert statement: {insert_stmt}");
224227
let mut reader = make_csv_reader(csv_import, file);
225228
let col_idxs = compute_column_indices(&mut reader, csv_import).await?;
@@ -256,13 +259,13 @@ async fn compute_column_indices<R: AsyncRead + Unpin + Send>(
256259
Ok(col_idxs)
257260
}
258261

259-
fn create_insert_stmt(kind: AnyKind, csv_import: &CsvImport) -> String {
262+
fn create_insert_stmt(dbms: SupportedDatabase, csv_import: &CsvImport) -> String {
260263
let columns = csv_import.columns.join(", ");
261264
let placeholders = csv_import
262265
.columns
263266
.iter()
264267
.enumerate()
265-
.map(|(i, _)| make_placeholder(kind, i + 1))
268+
.map(|(i, _)| make_placeholder(dbms, i + 1))
266269
.fold(String::new(), |mut acc, f| {
267270
if !acc.is_empty() {
268271
acc.push_str(", ");
@@ -328,7 +331,7 @@ fn test_make_statement() {
328331
escape: None,
329332
uploaded_file: "my_file.csv".into(),
330333
};
331-
let insert_stmt = create_insert_stmt(AnyKind::Postgres, &csv_import);
334+
let insert_stmt = create_insert_stmt(SupportedDatabase::Postgres, &csv_import);
332335
assert_eq!(
333336
insert_stmt,
334337
"INSERT INTO my_table (col1, col2) VALUES ($1, $2)"

src/webserver/database/execute_queries.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub fn stream_query_results_with_conn<'a>(
5454
ParsedStatement::CsvImport(csv_import) => {
5555
let connection = take_connection(&request.app_state.db, db_connection).await?;
5656
log::debug!("Executing CSV import: {csv_import:?}");
57-
run_csv_import(connection, csv_import, request).await.with_context(|| format!("Failed to import the CSV file {:?} into the table {:?}", csv_import.uploaded_file, csv_import.table_name))?;
57+
run_csv_import(connection, request.app_state.db.database_type, csv_import, request).await.with_context(|| format!("Failed to import the CSV file {:?} into the table {:?}", csv_import.uploaded_file, csv_import.table_name))?;
5858
},
5959
ParsedStatement::StmtWithParams(stmt) => {
6060
let query = bind_parameters(stmt, request, db_connection).await?;

0 commit comments

Comments
 (0)