Skip to content

Commit 0e7defa

Browse files
authored
Add SQLite extension entrypoint config to sqlx.toml, update SQLite extension example (#4107)
* Add SQLite extension entrypoint config to `sqlx.toml`, update SQLite extension example * Fix tests & formatting
1 parent 543d751 commit 0e7defa

File tree

8 files changed

+90
-29
lines changed

8 files changed

+90
-29
lines changed

examples/sqlite/extension/download-extension.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@
66
# directory on the library search path, either by using the system
77
# package manager or by compiling and installing it yourself.
88

9-
mkdir /tmp/sqlite3-lib && wget -O /tmp/sqlite3-lib/ipaddr.so https://github.com/nalgeon/sqlean/releases/download/0.15.2/ipaddr.so
9+
mkdir /tmp/sqlite3-lib && \
10+
wget -O /tmp/sqlean-linux-x64.zip https://github.com/nalgeon/sqlean/releases/download/0.28.0/sqlean-linux-x64.zip && \
11+
unzip /tmp/sqlean-linux-x64.zip -d /tmp/sqlite3-lib && \
12+
mv /tmp/sqlite3-lib/uuid.so /tmp/sqlite3-lib/uuid_renamed.so && \
13+
rm /tmp/sqlean-linux-x64.zip
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
create table uuids (uuid text);
2+
3+
-- The `uuid4` function is provided by the
4+
-- [uuid](https://github.com/nalgeon/sqlean/blob/main/docs/uuid.md)
5+
-- sqlite extension, and so this migration can not run if that
6+
-- extension is not loaded.
7+
insert into uuids (uuid) values
8+
(uuid4()),
9+
(uuid4()),
10+
(uuid4());

examples/sqlite/extension/sqlx.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[common.drivers.sqlite]
1+
[drivers.sqlite]
22
# Including the full path to the extension is somewhat unusual,
33
# because normally an extension will be installed in a standard
44
# directory which is part of the library search path. If that were the
@@ -9,4 +9,7 @@
99
# * Provide the full path the the extension, as seen below.
1010
# * Add the non-standard location to the library search path, which on
1111
# Linux means adding it to the LD_LIBRARY_PATH environment variable.
12-
unsafe-load-extensions = ["/tmp/sqlite3-lib/ipaddr"]
12+
unsafe-load-extensions = [
13+
"/tmp/sqlite3-lib/ipaddr",
14+
{ path = "/tmp/sqlite3-lib/uuid_renamed", entrypoint = "sqlite3_uuid_init" },
15+
]

examples/sqlite/extension/src/main.rs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,32 @@ use sqlx::{
77

88
#[tokio::main(flavor = "current_thread")]
99
async fn main() -> anyhow::Result<()> {
10-
let opts = SqliteConnectOptions::from_str(&std::env::var("DATABASE_URL")?)?
11-
// The sqlx.toml file controls loading extensions for the CLI
12-
// and for the query checking macros, *not* for the
13-
// application while it's running. Thus, if we want the
14-
// extension to be available during program execution, we need
15-
// to load it.
16-
//
17-
// Note that while in this case the extension path is the same
18-
// when checking the program (sqlx.toml) and when running it
19-
// (here), this is not required. The runtime environment can
20-
// be entirely different from the development one.
21-
//
22-
// The extension can be described with a full path, as seen
23-
// here, but in many cases that will not be necessary. As long
24-
// as the extension is installed in a directory on the library
25-
// search path, it is sufficient to just provide the extension
26-
// name, like "ipaddr"
27-
.extension("/tmp/sqlite3-lib/ipaddr");
10+
let opts = SqliteConnectOptions::from_str(&std::env::var("DATABASE_URL")?)?;
11+
// The sqlx.toml file controls loading extensions for the CLI
12+
// and for the query checking macros, *not* for the
13+
// application while it's running. Thus, if we want the
14+
// extension to be available during program execution, we need
15+
// to load it.
16+
//
17+
// Note that while in this case the extension paths are the
18+
// same when checking the program (sqlx.toml) and when running
19+
// it (here), this is not required. The runtime environment
20+
// can be entirely different from the development one.
21+
//
22+
// The extension can be described with a full path, as seen
23+
// here, but in many cases that will not be necessary. As long
24+
// as the extension is installed in a directory on the library
25+
// search path, it is sufficient to just provide the extension
26+
// name, like "ipaddr"
27+
let opts = unsafe { opts.extension("/tmp/sqlite3-lib/ipaddr") };
28+
// The entrypoint for an extension is usually inferred as
29+
// `sqlite3_extension_init` or `sqlite3_X_init` where X is the
30+
// lowercase, ASCII-only equivalent of the filename. For the
31+
// extension below, this would be `sqlite3_uuidrenamed_init`.
32+
// The entrypoint can instead be explicitly provided.
33+
let opts = unsafe {
34+
opts.extension_with_entrypoint("/tmp/sqlite3-lib/uuid_renamed", "sqlite3_uuid_init")
35+
};
2836

2937
let db = SqlitePool::connect_with(opts).await?;
3038

@@ -41,7 +49,11 @@ async fn main() -> anyhow::Result<()> {
4149
.execute(&db)
4250
.await?;
4351

44-
println!("Query which requires the extension was successfully executed.");
52+
query!("insert into uuids (uuid) values (uuid4())")
53+
.execute(&db)
54+
.await?;
55+
56+
println!("Queries which require the extensions were successfully executed.");
4557

4658
Ok(())
4759
}

sqlx-core/src/config/drivers.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,19 @@ pub struct SqliteConfig {
102102
/// [common.drivers.sqlite]
103103
/// unsafe-load-extensions = ["uuid", "vsv"]
104104
/// ```
105-
pub unsafe_load_extensions: Vec<String>,
105+
pub unsafe_load_extensions: Vec<SqliteExtension>,
106+
}
107+
108+
/// Extension for the SQLite database driver.
109+
#[derive(Debug, PartialEq)]
110+
#[cfg_attr(
111+
feature = "sqlx-toml",
112+
derive(serde::Deserialize),
113+
serde(untagged, deny_unknown_fields)
114+
)]
115+
pub enum SqliteExtension {
116+
Path(String),
117+
PathWithEntrypoint { path: String, entrypoint: String },
106118
}
107119

108120
/// Configuration for external database drivers.

sqlx-core/src/config/reference.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ database-url-var = "FOO_DATABASE_URL"
4444
# It is not possible to provide a truly safe version of this API.
4545
#
4646
# Use this field with care, and only load extensions that you trust.
47-
unsafe-load-extensions = ["uuid", "vsv"]
47+
unsafe-load-extensions = [
48+
"uuid",
49+
{ path = "vsv_renamed", entrypoint = "sqlite3_vsv_init" },
50+
]
4851

4952
# Configure external drivers in macros and sqlx-cli.
5053
#
@@ -90,8 +93,8 @@ numeric = "rust_decimal"
9093
# or not. They only override the inner type used.
9194
[macros.type-overrides]
9295
# Override a built-in type (map all `UUID` columns to `crate::types::MyUuid`)
93-
# Note: currently, the case of the type name MUST match.
94-
# Built-in types are spelled in all-uppercase to match SQL convention.
96+
# Note: currently, the case of the type name MUST match.
97+
# Built-in types are spelled in all-uppercase to match SQL convention.
9598
'UUID' = "crate::types::MyUuid"
9699

97100
# Support an external or custom wrapper type (e.g. from the `isn` Postgres extension)

sqlx-core/src/config/tests.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,16 @@ fn assert_common_config(config: &config::common::Config) {
1818
}
1919

2020
fn assert_drivers_config(config: &config::drivers::Config) {
21-
assert_eq!(config.sqlite.unsafe_load_extensions, ["uuid", "vsv"]);
21+
assert_eq!(
22+
config.sqlite.unsafe_load_extensions,
23+
vec![
24+
config::drivers::SqliteExtension::Path("uuid".to_string()),
25+
config::drivers::SqliteExtension::PathWithEntrypoint {
26+
path: "vsv_renamed".to_string(),
27+
entrypoint: "sqlite3_vsv_init".to_string()
28+
}
29+
]
30+
);
2231

2332
#[derive(Debug, Eq, PartialEq, serde::Deserialize)]
2433
#[serde(rename_all = "kebab-case")]

sqlx-sqlite/src/options/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ impl SqliteConnectOptions {
511511
/// .extension("vsv")
512512
/// .extension("mod_spatialite");
513513
/// }
514-
///
514+
///
515515
/// # Ok(options)
516516
/// # }
517517
/// ```
@@ -646,7 +646,15 @@ impl SqliteConnectOptions {
646646
#[cfg(feature = "load-extension")]
647647
for extension in &config.unsafe_load_extensions {
648648
// SAFETY: the documentation warns the user about loading extensions
649-
self = unsafe { self.extension(extension.clone()) };
649+
match extension {
650+
config::drivers::SqliteExtension::Path(path) => {
651+
self = unsafe { self.extension(path.clone()) }
652+
}
653+
config::drivers::SqliteExtension::PathWithEntrypoint { path, entrypoint } => {
654+
self =
655+
unsafe { self.extension_with_entrypoint(path.clone(), entrypoint.clone()) }
656+
}
657+
}
650658
}
651659

652660
#[cfg(not(feature = "load-extension"))]

0 commit comments

Comments
 (0)