#[cfg(test)]
mod test {
use crate::{Connection, Result};
#[cfg(feature = "bundled")]
#[test]
fn test_bundled_extensions_registered() -> Result<()> {
let config = crate::Config::default()
.with("autoinstall_known_extensions", "false")?
.with("autoload_known_extensions", "false")?;
let connection = Connection::open_in_memory_with_flags(config)?;
let extensions = [
"core_functions",
#[cfg(feature = "json")]
"json",
#[cfg(feature = "parquet")]
"parquet",
];
for extension in extensions {
connection.execute_batch(&format!("LOAD {extension};"))?;
let is_builtin = connection.query_row(
"SELECT installed AND loaded AND install_mode = 'STATICALLY_LINKED' AND install_path = '(BUILT-IN)' \
FROM duckdb_extensions() WHERE extension_name = ?",
[extension],
|row| row.get::<_, bool>(0),
)?;
assert!(is_builtin, "{extension} should be statically linked");
}
Ok(())
}
#[cfg(feature = "json")]
#[test]
fn test_extension_json() -> Result<()> {
let db = Connection::open_in_memory()?;
assert_eq!(
4,
db.query_row::<i32, _, _>(
r#"SELECT json_array_length('["duck","goose","swan",null]');"#,
[],
|r| r.get(0)
)?
);
Ok(())
}
#[cfg(feature = "parquet")]
#[test]
fn test_extension_parquet() -> Result<()> {
let db = Connection::open_in_memory()?;
assert_eq!(
300f32,
db.query_row::<f32, _, _>(
r#"SELECT SUM(value) FROM read_parquet('./examples/int32_decimal.parquet');"#,
[],
|r| r.get(0)
)?
);
Ok(())
}
#[cfg(feature = "icu")]
#[test]
fn test_extension_icu() -> Result<()> {
let db = Connection::open_in_memory()?;
assert_eq!(
1i64,
db.query_row::<i64, _, _>(
"SELECT count(*) FROM icu_calendar_names() WHERE name = 'gregorian';",
[],
|r| r.get(0)
)?
);
assert!(db.query_row::<bool, _, _>("SELECT length(icu_sort_key('Ş', 'ro')) > 0;", [], |r| r.get(0))?);
Ok(())
}
#[cfg(feature = "bundled-cmake")]
#[test]
#[ignore = "requires external extension loading to be enabled at build time"]
fn test_external_extension_load() -> Result<(), Box<dyn std::error::Error>> {
let directory = tempfile::tempdir()?;
let path = directory.path().join("missing.duckdb_extension");
let connection = Connection::open_in_memory()?;
let error = connection
.execute_batch(&format!("LOAD '{}';", path.to_string_lossy().replace('\'', "''")))
.expect_err("missing extension should not load")
.to_string();
assert!(
error.contains("not found"),
"expected a missing file error, got: {error}"
);
Ok(())
}
#[cfg(feature = "httpfs")]
#[test]
fn test_extension_httpfs() -> Result<(), Box<dyn std::error::Error>> {
use crate::{Config, OptionalExt};
let extension_directory = tempfile::tempdir()?;
let config = Config::default()
.with("extension_directory", extension_directory.path().to_string_lossy())?
.with("autoinstall_known_extensions", "false")?
.with("autoload_known_extensions", "false")?;
let connection = Connection::open_in_memory_with_flags(config)?;
connection.execute_batch("LOAD httpfs;")?;
let extension_state = connection
.query_row(
"SELECT installed, loaded, install_mode, install_path FROM duckdb_extensions() WHERE extension_name = 'httpfs'",
[],
|row| {
Ok((
row.get::<_, bool>(0)?,
row.get::<_, bool>(1)?,
row.get::<_, String>(2)?,
row.get::<_, Option<String>>(3)?,
))
},
)
.optional()?
.expect("statically linked httpfs should be listed");
assert_eq!(
extension_state,
(
true,
true,
"STATICALLY_LINKED".to_owned(),
Some("(BUILT-IN)".to_owned())
),
"statically linked httpfs should load without an installed artifact"
);
assert!(
extension_directory.path().read_dir()?.next().is_none(),
"loading statically linked httpfs must not write an extension artifact"
);
Ok(())
}
}