Skip to content

Commit f9e9be6

Browse files
cursoragentlovasoa
andcommitted
Add ODBC support and update dependencies
This commit introduces support for ODBC databases, allowing users to connect to a wider range of data sources. It also includes updates to various dependencies, ensuring the project is using the latest stable versions and incorporating necessary bug fixes and performance improvements. Co-authored-by: contact <[email protected]>
1 parent 1c64fd2 commit f9e9be6

File tree

8 files changed

+826
-31
lines changed

8 files changed

+826
-31
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ panic = "abort"
2323
codegen-units = 2
2424

2525
[dependencies]
26-
sqlx = { package = "sqlx-oldapi", version = "0.6.48", default-features = false, features = [
26+
sqlx = { package = "sqlx-oldapi", version = "0.6.49-beta", default-features = false, features = [
2727
"any",
2828
"runtime-tokio-rustls",
2929
"migrate",
3030
"sqlite",
3131
"postgres",
3232
"mysql",
3333
"mssql",
34+
"odbc",
3435
"chrono",
3536
"json",
3637
"uuid",

src/webserver/database/connect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl Database {
6161
} else {
6262
// Different databases have a different number of max concurrent connections allowed by default
6363
match db_kind {
64-
AnyKind::Postgres => 50,
64+
AnyKind::Postgres | AnyKind::Odbc => 50, // Default to PostgreSQL-like limits for ODBC
6565
AnyKind::MySql => 75,
6666
AnyKind::Sqlite => {
6767
if config.database_url.contains(":memory:") {

src/webserver/database/error_highlighting.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub fn display_stmt_db_error(
8686
anyhow::Error::new(NiceDatabaseError {
8787
source_file: source_file.to_path_buf(),
8888
db_err,
89-
query: stmt.query.to_string(),
89+
query: stmt.query.clone(),
9090
query_position: Some(stmt.query_position),
9191
})
9292
}

src/webserver/database/mod.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,90 @@ pub use sql::ParsedSqlFile;
1414
use sql::{DbPlaceHolder, DB_PLACEHOLDERS};
1515
use sqlx::any::AnyKind;
1616

17+
/// Supported database types in `SQLPage`
18+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19+
pub enum SupportedDatabase {
20+
Sqlite,
21+
Postgres,
22+
MySql,
23+
Mssql,
24+
Odbc,
25+
}
26+
27+
impl SupportedDatabase {
28+
/// Detect the database type from a connection's `dbms_name`
29+
#[must_use]
30+
pub fn from_dbms_name(dbms_name: &str) -> Option<Self> {
31+
match dbms_name.to_lowercase().as_str() {
32+
"sqlite" | "sqlite3" => Some(Self::Sqlite),
33+
"postgres" | "postgresql" => Some(Self::Postgres),
34+
"mysql" | "mariadb" => Some(Self::MySql),
35+
"mssql" | "sql server" | "microsoft sql server" => Some(Self::Mssql),
36+
"odbc" => Some(Self::Odbc),
37+
_ => None,
38+
}
39+
}
40+
41+
/// Convert from sqlx `AnyKind` to our enum
42+
#[must_use]
43+
pub fn from_any_kind(kind: AnyKind) -> Self {
44+
match kind {
45+
AnyKind::Sqlite => Self::Sqlite,
46+
AnyKind::Postgres => Self::Postgres,
47+
AnyKind::MySql => Self::MySql,
48+
AnyKind::Mssql => Self::Mssql,
49+
AnyKind::Odbc => Self::Odbc,
50+
}
51+
}
52+
53+
/// Get the display name for the database
54+
#[must_use]
55+
pub fn display_name(self) -> &'static str {
56+
match self {
57+
Self::Sqlite => "SQLite",
58+
Self::Postgres => "PostgreSQL",
59+
Self::MySql => "MySQL",
60+
Self::Mssql => "Microsoft SQL Server",
61+
Self::Odbc => "ODBC",
62+
}
63+
}
64+
}
65+
1766
pub struct Database {
1867
pub connection: sqlx::AnyPool,
1968
}
69+
2070
impl Database {
2171
pub async fn close(&self) -> anyhow::Result<()> {
2272
log::info!("Closing all database connections...");
2373
self.connection.close().await;
2474
Ok(())
2575
}
76+
77+
/// Detect the database type using the connection's `dbms_name`
78+
pub async fn detect_database_type(&self) -> anyhow::Result<SupportedDatabase> {
79+
let mut conn = self.connection.acquire().await?;
80+
let dbms_name = conn.dbms_name().await?;
81+
82+
if let Some(db_type) = SupportedDatabase::from_dbms_name(&dbms_name) {
83+
log::debug!(
84+
"Detected database type: {} from dbms_name: {}",
85+
db_type.display_name(),
86+
dbms_name
87+
);
88+
Ok(db_type)
89+
} else {
90+
log::warn!("Unknown database type from dbms_name: {dbms_name}");
91+
// Fallback to AnyKind detection
92+
Ok(SupportedDatabase::from_any_kind(self.connection.any_kind()))
93+
}
94+
}
95+
96+
/// Get the database type using the fallback method (`AnyKind`)
97+
#[must_use]
98+
pub fn get_database_type(&self) -> SupportedDatabase {
99+
SupportedDatabase::from_any_kind(self.connection.any_kind())
100+
}
26101
}
27102

28103
#[derive(Debug)]

src/webserver/database/sql.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ fn syntax_error(err: ParserError, parser: &Parser, sql: &str) -> ParsedStatement
239239

240240
fn dialect_for_db(db_kind: AnyKind) -> Box<dyn Dialect> {
241241
match db_kind {
242-
AnyKind::Postgres => Box::new(PostgreSqlDialect {}),
242+
AnyKind::Postgres | AnyKind::Odbc => Box::new(PostgreSqlDialect {}), // Default to PostgreSQL dialect for ODBC
243243
AnyKind::Mssql => Box::new(MsSqlDialect {}),
244244
AnyKind::MySql => Box::new(MySqlDialect {}),
245245
AnyKind::Sqlite => Box::new(SQLiteDialect {}),

src/webserver/database/sqlpage_functions/functions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ async fn user_info<'a>(
768768
"iat" => Some(claims.issue_time().timestamp().to_string()),
769769
"sub" => Some(claims.subject().to_string()),
770770
"auth_time" => claims.auth_time().map(|t| t.timestamp().to_string()),
771-
"nonce" => claims.nonce().map(|n| n.secret().to_string()), // Assuming Nonce has secret()
771+
"nonce" => claims.nonce().map(|n| n.secret().clone()), // Assuming Nonce has secret()
772772
"acr" => claims.auth_context_ref().map(|acr| acr.to_string()),
773773
// amr requires serialization: handled separately if needed
774774
"azp" => claims.authorized_party().map(|azp| azp.to_string()),

src/webserver/database/syntax_tree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ pub(super) async fn extract_req_param<'a>(
177177
}
178178
}
179179
StmtParam::Error(x) => anyhow::bail!("{}", x),
180-
StmtParam::Literal(x) => Some(Cow::Owned(x.to_string())),
180+
StmtParam::Literal(x) => Some(Cow::Owned(x.clone())),
181181
StmtParam::Null => None,
182182
StmtParam::Concat(args) => concat_params(&args[..], request, db_connection).await?,
183183
StmtParam::JsonObject(args) => {

0 commit comments

Comments
 (0)