quack-rs 0.18 follows 0.16 directly; 0.17 was never published. Most of its
breaking changes close holes where safe code could crash DuckDB, return a wrong
answer or abort the process. Many need no change in a correct extension. This
page lists each change marked Breaking: in the
changelog, grouped by area, with what to change.
To upgrade, set the version in Cargo.toml, build, and work through the
compiler errors with this page open:
[dependencies]
quack-rs = "0.18"
Then run your SQL tests. A few changes are behavioural (a registration that used
to succeed now returns an error) and show up only at run time.
A *_callback! body must have type () (a cast_callback! body still returns bool).
Remove ? and trailing Ok(()) from bodies. Report errors through the callback's set_error and return.
A macro body is no longer an unsafe context.
Wrap each unsafe call or raw-pointer read in its own unsafe { … } block.
Scalar bind and init callbacks take RawScalarBindInfo / RawScalarInitInfo (in ScalarBindFn, ScalarInitFn, scalar_bind_callback!, scalar_init_callback!, ScalarBindInfo::new, ScalarInitInfo::new).
For a hand-written scalar bind/init extern "C" fn, change the parameter type to RawScalarBindInfo / RawScalarInitInfo. Use scalar_bind_callback! / scalar_init_callback!, never the table_* macros, for scalar functions.
ScalarFunctionBuilder::register and ScalarFunctionSetBuilder::register (and map1, map2, …) refuse a parameter signature duckdb_functions() already lists under that name, built-ins included.
Rename the function, or change its signature, if registration now fails. Such an overload used to silently replace the existing one.
Overloads that DuckDB's binder would find ambiguous with varargs are refused at registration.
Remove or change the overlapping overload.
validate_function_name refuses the 53 keywords DuckDB cannot call unquoted (DUCKDB_UNCALLABLE_KEYWORDS); SqlMacro parameters are checked with validate_parameter_name.
Rename a function called, for example, coalesce, or a macro parameter that is a keyword.
On DuckDB 1.5.0 to 1.5.4, ScalarBindInfo::argument returns nothing and fails the bind with an explanation.
Do not rely on bind-time argument inspection on those releases, or require DuckDB 1.5.5+.
The STRUCT and UNION constructors (struct_type, struct_type_from_logical, union_type, union_type_from_logical) refuse duplicate names (ignoring ASCII case) and more than MAX_UNION_MEMBERS members; the plain forms panic.
Make field and member names unique, or use the try_ forms and handle the error.
try_array(_, 0), an empty union_type and an empty Value::array_value are refused.
Do not build zero-size arrays or empty unions; DuckDB's SQL refuses them too.
TypeId::Varint.sql_name() returns "BIGNUM" instead of "VARINT".
VectorWriter::set_null / set_null_range (and DataChunk::propagate_nulls) on a STRUCT or ARRAY vector also null the row's fields or elements, recursively.
To reuse such a row, call set_valid on it first, then write the fields.
VectorWriter::write_varchar / write_blob and StructWriter::write_varchar / write_blob panic for a value longer than MAX_STRING_LEN.
Use try_write_varchar / try_write_blob to get an error instead, or keep values under 4 GiB.
SelectionVector::new returns Result<Self, ExtensionError> and zeroes the buffer.
Add ?.
datetime::date_to_days, time_from_micros, time_tz_from_bits, timestamp_from_micros, timestamp_to_micros, time_tz_bits and decimal_to_f64 return Option.
Write while let Some(chunk) = result.next_chunk()? { … }.
Appender refuses to lose rows: a failed row() poisons it; close() with an unfinished row, any mutation after close(), and append_chunk in the middle of a row are errors.
Handle the errors these now return. With duckdb-1-5, clear() resets the appender and clears the poison.
CatalogEntry::lookup and Catalog::get_entry return Result<Option<CatalogEntry>, ExtensionError>.
Treat Ok(None) as "not found" and Err as "refused".
Lookups are refused for Schema, Database, PreparedStatement and Invalid; for a Type or Collation name DuckDB would autoload an extension for, while autoload_known_extensions is on; and in any catalog whose type is not "duckdb".
Query those with SQL, or SET autoload_known_extensions = false before an autoloading name.
CatalogEntry copies the name and type at lookup and holds no handle.
No change for name() / entry_type(); stop relying on the entry's handle.
FileSystem<'ctx> borrows its ClientContext; FileSystem::open returns FileHandle<'fs>, which borrows the FileSystem.
Add lifetime parameters where you store them, and keep the context and file system alive longer than the handles.
ConfigOptionBuilder::register refuses an option with no default, a pseudo-type (ANY, SQLNULL, INTEGER_LITERAL, STRING_LITERAL) or a type it cannot build a default for (BIGNUM, GEOMETRY, VARIANT, …), and a name duckdb_settings() already lists (case-insensitive, aliases included).
Call default_value(...), use a concrete type (VARCHAR if in doubt), and prefix the name with your extension's name.
MockVectorWriter behaves like a real output vector: set_null then a write leaves the row NULL, an unwritten row is valid (check is_written), a write past capacity panics, a string over MAX_STRING_LEN panics.
Fix the callback the failing test exposes, or update the test's expectations.
MockRegistrar runs the same checks as real registration (missing callback or return type, empty set, incomplete copy function, config option without type or default) and records nothing on failure.
The [0.18.0] section of the changelog marks 54 entries Breaking:. Nine of
them repeat a change listed under Changed (in Fixed or Security, with "see
Changed"), so this page lists 45 distinct changes.