Skip to content

Commit 503a36e

Browse files
VictorKoendersabonander
authored andcommitted
Added regexp support in sqlite (launchbadge#2189)
* 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 <[email protected]> Co-authored-by: Victor Koenders <[email protected]>
1 parent 237a082 commit 503a36e

File tree

8 files changed

+298
-10
lines changed

8 files changed

+298
-10
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ mac_address = ["sqlx-core/mac_address", "sqlx-macros?/mac_address", "sqlx-postgr
109109
rust_decimal = ["sqlx-core/rust_decimal", "sqlx-macros?/rust_decimal", "sqlx-mysql?/rust_decimal", "sqlx-postgres?/rust_decimal"]
110110
time = ["sqlx-core/time", "sqlx-macros?/time", "sqlx-mysql?/time", "sqlx-postgres?/time", "sqlx-sqlite?/time"]
111111
uuid = ["sqlx-core/uuid", "sqlx-macros?/uuid", "sqlx-mysql?/uuid", "sqlx-postgres?/uuid", "sqlx-sqlite?/uuid"]
112+
regexp = ["sqlx-sqlite?/regexp"]
112113

113114
[workspace.dependencies]
114115
# Driver crates

sqlx-core/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ futures-intrusive = "0.4.0"
7070
futures-util = { version = "0.3.19", default-features = false, features = ["alloc", "sink", "io"] }
7171
generic-array = { version = "0.14.4", default-features = false, optional = true }
7272
hex = "0.4.3"
73-
7473
log = { version = "0.4.14", default-features = false }
7574
memchr = { version = "2.4.1", default-features = false }
7675
num-bigint = { version = "0.4.0", default-features = false, optional = true, features = ["std"] }

sqlx-sqlite/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ offline = ["sqlx-core/offline", "serde"]
1818
migrate = ["sqlx-core/migrate"]
1919

2020
chrono = ["dep:chrono", "bitflags"]
21+
regexp = ["dep:regex"]
2122

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

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

4850
[dependencies.libsqlite3-sys]
4951
version = "0.25.1"
@@ -58,3 +60,6 @@ features = [
5860
[dependencies.sqlx-core]
5961
version = "=0.6.2"
6062
path = "../sqlx-core"
63+
64+
[dev-dependencies]
65+
sqlx = { version = "0.6.2", path = "..", default-features = false, features = ["macros", "runtime-tokio", "tls-none"] }

sqlx-sqlite/src/connection/establish.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pub struct EstablishParams {
4444
extensions: IndexMap<CString, Option<CString>>,
4545
pub(crate) thread_name: String,
4646
pub(crate) command_channel_size: usize,
47+
#[cfg(feature = "regexp")]
48+
register_regexp_function: bool,
4749
}
4850

4951
impl EstablishParams {
@@ -145,6 +147,8 @@ impl EstablishParams {
145147
extensions,
146148
thread_name: (options.thread_name)(THREAD_ID.fetch_add(1, Ordering::AcqRel)),
147149
command_channel_size: options.command_channel_size,
150+
#[cfg(feature = "regexp")]
151+
register_regexp_function: options.register_regexp_function,
148152
})
149153
}
150154

@@ -238,12 +242,10 @@ impl EstablishParams {
238242
&err_msg,
239243
))));
240244
}
241-
}
242-
243-
// Preempt any hypothetical security issues arising from leaving ENABLE_LOAD_EXTENSION
244-
// on by disabling the flag again once we've loaded all the requested modules.
245-
// Fail-fast (via `?`) if disabling the extension loader didn't work for some reason,
246-
// avoids an unexpected state going undetected.
245+
} // Preempt any hypothetical security issues arising from leaving ENABLE_LOAD_EXTENSION
246+
// on by disabling the flag again once we've loaded all the requested modules.
247+
// Fail-fast (via `?`) if disabling the extension loader didn't work for some reason,
248+
// avoids an unexpected state going undetected.
247249
unsafe {
248250
Self::sqlite3_set_load_extension(
249251
handle.as_ptr(),
@@ -252,6 +254,15 @@ impl EstablishParams {
252254
}
253255
}
254256

257+
#[cfg(feature = "regexp")]
258+
if self.register_regexp_function {
259+
// configure a `regexp` function for sqlite, it does not come with one by default
260+
let status = crate::regexp::register(handle.as_ptr());
261+
if status != SQLITE_OK {
262+
return Err(Error::Database(Box::new(SqliteError::new(handle.as_ptr()))));
263+
}
264+
}
265+
255266
// Configure a busy timeout
256267
// This causes SQLite to automatically sleep in increasing intervals until the time
257268
// when there is something locked during [sqlite3_step].

sqlx-sqlite/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ mod value;
7272
#[cfg(feature = "any")]
7373
pub mod any;
7474

75+
#[cfg(feature = "regexp")]
76+
mod regexp;
77+
7578
#[cfg(feature = "migrate")]
7679
mod migrate;
7780

sqlx-sqlite/src/options/mod.rs

+33-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use sqlx_core::IndexMap;
3838
/// ```rust,no_run
3939
/// # use sqlx_core::connection::ConnectOptions;
4040
/// # use sqlx_core::error::Error;
41-
/// use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode};
41+
/// # use sqlx_sqlite::{SqliteConnectOptions, SqliteJournalMode};
4242
/// use std::str::FromStr;
4343
///
4444
/// # fn main() {
@@ -79,6 +79,9 @@ pub struct SqliteConnectOptions {
7979

8080
pub(crate) serialized: bool,
8181
pub(crate) thread_name: Arc<DebugFn<dyn Fn(u64) -> String + Send + Sync + 'static>>,
82+
83+
#[cfg(feature = "regexp")]
84+
pub(crate) register_regexp_function: bool,
8285
}
8386

8487
impl Default for SqliteConnectOptions {
@@ -185,6 +188,8 @@ impl SqliteConnectOptions {
185188
thread_name: Arc::new(DebugFn(|id| format!("sqlx-sqlite-worker-{}", id))),
186189
command_channel_size: 50,
187190
row_channel_size: 50,
191+
#[cfg(feature = "regexp")]
192+
register_regexp_function: false,
188193
}
189194
}
190195

@@ -431,8 +436,8 @@ impl SqliteConnectOptions {
431436
/// will be loaded in the order they are added.
432437
/// ```rust,no_run
433438
/// # use sqlx_core::error::Error;
434-
/// use std::str::FromStr;
435-
/// use sqlx::sqlite::SqliteConnectOptions;
439+
/// # use std::str::FromStr;
440+
/// # use sqlx_sqlite::SqliteConnectOptions;
436441
/// # fn options() -> Result<SqliteConnectOptions, Error> {
437442
/// let options = SqliteConnectOptions::from_str("sqlite://data.db")?
438443
/// .extension("vsv")
@@ -458,4 +463,29 @@ impl SqliteConnectOptions {
458463
.insert(extension_name.into(), Some(entry_point.into()));
459464
self
460465
}
466+
467+
/// Register a regexp function that allows using regular expressions in queries.
468+
///
469+
/// ```
470+
/// # use std::str::FromStr;
471+
/// # use sqlx::{ConnectOptions, Connection, Row};
472+
/// # use sqlx_sqlite::SqliteConnectOptions;
473+
/// # async fn run() -> sqlx::Result<()> {
474+
/// let mut sqlite = SqliteConnectOptions::from_str("sqlite://:memory:")?
475+
/// .with_regexp()
476+
/// .connect()
477+
/// .await?;
478+
/// let tables = sqlx::query("SELECT name FROM sqlite_schema WHERE name REGEXP 'foo(\\d+)bar'")
479+
/// .fetch_all(&mut sqlite)
480+
/// .await?;
481+
/// # Ok(())
482+
/// # }
483+
/// ```
484+
///
485+
/// This uses the [`regex`] crate, and is only enabled when you enable the `regex` feature is enabled on sqlx
486+
#[cfg(feature = "regexp")]
487+
pub fn with_regexp(mut self) -> Self {
488+
self.register_regexp_function = true;
489+
self
490+
}
461491
}

0 commit comments

Comments
 (0)