All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Migrated FFI layer to
quack-rsv0.5.0 (crates.io) SDK for safe state management, vector I/O, and function set registration - All 6 aggregate functions now use
AggregateFunctionSetBuilderfor registration retentionandsequence_match_eventsusereturns_logical(LogicalType::list(...))— eliminating the last raw function set registration code- 18 new
AggregateTestHarnessunit tests for combine config-propagation across all 5 aggregate functions (435 → 453 tests)
- Entry point (
src/lib.rs) now usesquack_rs::entry_point!macro instead of ~80 lines of hand-rolled unsafe code - MSRV bumped from 1.80 to 1.84.1 (required by quack-rs)
- FFI modules use
FfiState<T>,VectorReader,VectorWriterfrom quack-rs - LIST output uses
ListVector+VectorWriterinstead of raw pointer arithmetic - VARCHAR output uses
VectorWriter::write_varchar()instead ofCString+ raw FFI - LIST type construction uses
LogicalType::list()instead of rawduckdb_create_list_type retentionregistration: ~45 lines of raw loop → 15-line builder chainsequence_match_eventsregistration: ~55 lines of raw loop → 15-line builder chainsessionizeFFI remains hand-rolled (window function limitation in quack-rs)
- Hand-rolled
read_varchar()helper insequence_next_node(replaced byVectorReader::read_str()) - Raw
duckdb_list_vector_*pointer arithmetic in retention and sequence_match_events (replaced byListVectorwrappers) - Raw
duckdb_create_aggregate_function_setloops in retention and sequence_match_events (replaced byAggregateFunctionSetBuilder) CStringsanitization in sequence_next_node (replaced bywrite_varchar)
0.2.0 - 2026-02-15
sequence_next_nodefunction with full direction (forward/backward) and base (head/tail/first_match/last_match) supportsequence_match_eventsfunction returning matched condition timestamps asLIST(TIMESTAMP)- 32-condition support for all variadic functions, matching ClickHouse's limit (expanded from 8 to 32)
- 6 combinable
window_funnelmodes:strict,strict_order,strict_deduplication,strict_increase,strict_once,allow_reentry - NFA fast-path classification dispatching common pattern shapes to
specialized O(n) linear scans (39--61% improvement for
sequence_count) - Presorted detection skipping O(n log n) sort for timestamp-ordered input
- 27 end-to-end SQL tests against real DuckDB v1.4.4 CLI
- 26 property-based tests (proptest) verifying algebraic properties
- GitHub Pages documentation site (mdBook) with function reference, use cases, FAQ, architecture, and performance documentation
- CI/CD pipeline with structured test output (
cargo-nextest), job summaries, E2E workflow, SemVer release validation, 4-platform release builds with provenance attestation - Community extension infrastructure (
description.yml,Makefile,extension-ci-toolssubmodule) - Listed in DuckDB Community Extensions
repository (PR #1306,
merged 2026-02-15). Install with
INSTALL behavioral FROM community; LOAD behavioral; 'timestamp_dedup'mode string for the extension-only timestamp-based deduplication mode inwindow_funnel- ClickHouse parity scope table and known semantic differences documentation
'strict_deduplication'mode mapping: Now correctly maps toSTRICT(0x01), matching ClickHouse where'strict'and'strict_deduplication'are aliases. The timestamp-based dedup behavior is now available under'timestamp_dedup'.- BREAKING: All public state structs marked
#[non_exhaustive]to allow future field additions without semver-breaking changes. Affected structs:SessionizeState,SessionizeBoundaryState,RetentionState,WindowFunnelState,SequenceState,SequenceNextNodeState,NextNodeEvent,Event,CompiledPattern,PatternError,MatchResult. Use the providednew()constructors instead of struct literal syntax. - Custom C entry point (
behavioral_init_c_api) replaces fragileduckdb::Connectionextraction, usingduckdb_connectdirectly fromduckdb_extension_access Arc<str>forSend+Syncsafety insequence_next_node(replacedRc<str>)- Defensive FFI boolean reading:
*const boolreplaced with*const u8across all FFI modules for ABI safety - No-panic FFI entry point: removed
.unwrap()on DuckDB function pointers inlib.rs - Runtime dependency reduced from 3 crates to 1 (
libduckdb-sysonly)
- SEGFAULT on extension load (incorrect pointer arithmetic in connection extraction)
- 6 of 7 functions silently failing to register (missing
duckdb_aggregate_function_set_namecall) window_funnelreturning incorrect results (combine not propagatingwindow_size_usandmode)sequence_next_nodeNULL output producing\0\0\0\0instead of NULL (missingduckdb_vector_ensure_validity_writablecall)- Interior null byte handling in
sequence_next_nodeFFI output retentionfinalize callingensure_validity_writableinside loop (hoisted outside)
0.1.0 - 2026-02-14
- Initial release with 7 behavioral analytics functions
sessionize-- window function assigning session IDs based on inactivity gapsretention-- aggregate function for cohort retention analysiswindow_funnel-- conversion funnel step tracking within a time windowsequence_match-- NFA-based pattern matching over event sequencessequence_count-- count non-overlapping pattern occurrencessequence_match_events-- return timestamps of matched pattern stepssequence_next_node-- find next/previous event value after pattern match- Complete ClickHouse behavioral analytics function parity
- 453 unit tests + 1 doc-test
- 27 E2E SQL integration tests
- Criterion.rs benchmarks for all 7 functions (up to 1 billion elements)
- 88.4% mutation testing kill rate (cargo-mutants)
- MIT license