Skip to content

Commit

Permalink
Added regexp support in sqlite (#2189)
Browse files Browse the repository at this point in the history
* CHANGELOG: mention that users should upgrade CLI

* Added regexp support in sqlite

* Added a with_regexp function to sqliteconnectoptions

* Fixed tests

* Undo CHANGELOG.md change

---------

Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
Co-authored-by: Victor Koenders <victor.koenders@qrtech.se>
  • Loading branch information
3 people committed Feb 18, 2023
1 parent b2d1068 commit 374fabc
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 10 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ mac_address = ["sqlx-core/mac_address", "sqlx-macros?/mac_address", "sqlx-postgr
rust_decimal = ["sqlx-core/rust_decimal", "sqlx-macros?/rust_decimal", "sqlx-mysql?/rust_decimal", "sqlx-postgres?/rust_decimal"]
time = ["sqlx-core/time", "sqlx-macros?/time", "sqlx-mysql?/time", "sqlx-postgres?/time", "sqlx-sqlite?/time"]
uuid = ["sqlx-core/uuid", "sqlx-macros?/uuid", "sqlx-mysql?/uuid", "sqlx-postgres?/uuid", "sqlx-sqlite?/uuid"]
regexp = ["sqlx-sqlite?/regexp"]

[workspace.dependencies]
# Driver crates
Expand Down
1 change: 0 additions & 1 deletion sqlx-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ futures-intrusive = "0.4.0"
futures-util = { version = "0.3.19", default-features = false, features = ["alloc", "sink", "io"] }
generic-array = { version = "0.14.4", default-features = false, optional = true }
hex = "0.4.3"

log = { version = "0.4.14", default-features = false }
memchr = { version = "2.4.1", default-features = false }
num-bigint = { version = "0.4.0", default-features = false, optional = true, features = ["std"] }
Expand Down
5 changes: 5 additions & 0 deletions sqlx-sqlite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ offline = ["sqlx-core/offline", "serde"]
migrate = ["sqlx-core/migrate"]

chrono = ["dep:chrono", "bitflags"]
regexp = ["dep:regex"]

[dependencies]
futures-core = { version = "0.3.19", default-features = false }
Expand All @@ -44,6 +45,7 @@ log = "0.4.17"
tracing = { version = "0.1.37", features = ["log"] }

serde = { version = "1.0.145", features = ["derive"], optional = true }
regex = { version = "1.5.5", optional = true }

[dependencies.libsqlite3-sys]
version = "0.25.1"
Expand All @@ -58,3 +60,6 @@ features = [
[dependencies.sqlx-core]
version = "=0.6.2"
path = "../sqlx-core"

[dev-dependencies]
sqlx = { version = "0.6.2", path = "..", default-features = false, features = ["macros", "runtime-tokio", "tls-none"] }
23 changes: 17 additions & 6 deletions sqlx-sqlite/src/connection/establish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub struct EstablishParams {
extensions: IndexMap<CString, Option<CString>>,
pub(crate) thread_name: String,
pub(crate) command_channel_size: usize,
#[cfg(feature = "regexp")]
register_regexp_function: bool,
}

impl EstablishParams {
Expand Down Expand Up @@ -145,6 +147,8 @@ impl EstablishParams {
extensions,
thread_name: (options.thread_name)(THREAD_ID.fetch_add(1, Ordering::AcqRel)),
command_channel_size: options.command_channel_size,
#[cfg(feature = "regexp")]
register_regexp_function: options.register_regexp_function,
})
}

Expand Down Expand Up @@ -238,12 +242,10 @@ impl EstablishParams {
&err_msg,
))));
}
}

// Preempt any hypothetical security issues arising from leaving ENABLE_LOAD_EXTENSION
// on by disabling the flag again once we've loaded all the requested modules.
// Fail-fast (via `?`) if disabling the extension loader didn't work for some reason,
// avoids an unexpected state going undetected.
} // Preempt any hypothetical security issues arising from leaving ENABLE_LOAD_EXTENSION
// on by disabling the flag again once we've loaded all the requested modules.
// Fail-fast (via `?`) if disabling the extension loader didn't work for some reason,
// avoids an unexpected state going undetected.
unsafe {
Self::sqlite3_set_load_extension(
handle.as_ptr(),
Expand All @@ -252,6 +254,15 @@ impl EstablishParams {
}
}

#[cfg(feature = "regexp")]
if self.register_regexp_function {
// configure a `regexp` function for sqlite, it does not come with one by default
let status = crate::regexp::register(handle.as_ptr());
if status != SQLITE_OK {
return Err(Error::Database(Box::new(SqliteError::new(handle.as_ptr()))));
}
}

// Configure a busy timeout
// This causes SQLite to automatically sleep in increasing intervals until the time
// when there is something locked during [sqlite3_step].
Expand Down
3 changes: 3 additions & 0 deletions sqlx-sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ mod value;
#[cfg(feature = "any")]
pub mod any;

#[cfg(feature = "regexp")]
mod regexp;

#[cfg(feature = "migrate")]
mod migrate;

Expand Down
36 changes: 33 additions & 3 deletions sqlx-sqlite/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use sqlx_core::IndexMap;
/// ```rust,no_run
/// # use sqlx_core::connection::ConnectOptions;
/// # use sqlx_core::error::Error;
/// use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode};
/// # use sqlx_sqlite::{SqliteConnectOptions, SqliteJournalMode};
/// use std::str::FromStr;
///
/// # fn main() {
Expand Down Expand Up @@ -79,6 +79,9 @@ pub struct SqliteConnectOptions {

pub(crate) serialized: bool,
pub(crate) thread_name: Arc<DebugFn<dyn Fn(u64) -> String + Send + Sync + 'static>>,

#[cfg(feature = "regexp")]
pub(crate) register_regexp_function: bool,
}

impl Default for SqliteConnectOptions {
Expand Down Expand Up @@ -185,6 +188,8 @@ impl SqliteConnectOptions {
thread_name: Arc::new(DebugFn(|id| format!("sqlx-sqlite-worker-{}", id))),
command_channel_size: 50,
row_channel_size: 50,
#[cfg(feature = "regexp")]
register_regexp_function: false,
}
}

Expand Down Expand Up @@ -431,8 +436,8 @@ impl SqliteConnectOptions {
/// will be loaded in the order they are added.
/// ```rust,no_run
/// # use sqlx_core::error::Error;
/// use std::str::FromStr;
/// use sqlx::sqlite::SqliteConnectOptions;
/// # use std::str::FromStr;
/// # use sqlx_sqlite::SqliteConnectOptions;
/// # fn options() -> Result<SqliteConnectOptions, Error> {
/// let options = SqliteConnectOptions::from_str("sqlite://data.db")?
/// .extension("vsv")
Expand All @@ -458,4 +463,29 @@ impl SqliteConnectOptions {
.insert(extension_name.into(), Some(entry_point.into()));
self
}

/// Register a regexp function that allows using regular expressions in queries.
///
/// ```
/// # use std::str::FromStr;
/// # use sqlx::{ConnectOptions, Connection, Row};
/// # use sqlx_sqlite::SqliteConnectOptions;
/// # async fn run() -> sqlx::Result<()> {
/// let mut sqlite = SqliteConnectOptions::from_str("sqlite://:memory:")?
/// .with_regexp()
/// .connect()
/// .await?;
/// let tables = sqlx::query("SELECT name FROM sqlite_schema WHERE name REGEXP 'foo(\\d+)bar'")
/// .fetch_all(&mut sqlite)
/// .await?;
/// # Ok(())
/// # }
/// ```
///
/// This uses the [`regex`] crate, and is only enabled when you enable the `regex` feature is enabled on sqlx
#[cfg(feature = "regexp")]
pub fn with_regexp(mut self) -> Self {
self.register_regexp_function = true;
self
}
}
Loading

0 comments on commit 374fabc

Please sign in to comment.