Coverage
The characterization checklist, item by item, per engine
The checklist of characterization/areas.json: for each item, the facts that settle it on each engine and the strongest evidence among them, or missing. python -m sverify_dev.facts --coverage <engine> prints the same.
| Engine | Items settled | Facts |
|---|---|---|
| DuckDB | 68 of 69 | 101 |
| Polars | 69 of 69 | 103 |
| DataFusion | 1 of 69 | 3 |
| Spark | 69 of 69 | 71 |
integers
decimals
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
decimal_rescale |
A result with more digits than its scale: how it rounds. | probe: duckdb-decimal-arith | probe: polars-decimal-arith, polars-decimal-casts | missing | probe: spark-decimal-arith |
decimal_result_type |
The precision and scale of decimal arithmetic and aggregates, and overflow past 38 digits. | probe: duckdb-decimal-arith | probe: polars-decimal-arith | missing | probe: spark-decimal-arith |
doubles
strings
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
char_set |
upper, lower: Unicode or ASCII mappings. |
probe: duckdb-case-mapping | probe: polars-case, polars-length, polars-reverse | missing | probe: spark-case, spark-string-plain |
case_sensitivity |
like, contains, starts_with, strpos, regexes: case-sensitive or not. |
probe: duckdb-F4, duckdb-string-match-case | probe: polars-match-functions, polars-regex | missing | fuzz: spark-match-functions, spark-regex |
substring_range |
substring offsets and lengths outside 32 bits. | probe: duckdb-substring-range | probe: polars-substring-range | missing | fuzz: spark-substring-range |
negative_start |
substring with a negative or zero start; its indexing (characters or bytes, 0- or 1-based). | engine_test: duckdb-R13 | probe: polars-strpos, polars-substring | missing | fuzz: spark-strpos, spark-substring |
string_positions |
What substring and strpos positions count: characters or bytes, from 0 or 1; strpos without a match. |
probe: duckdb-R13, duckdb-string-match-case | probe: polars-strpos, polars-substring | missing | fuzz: spark-strpos, spark-substring |
regex_dialect |
The regex engine and syntax, classes on non-ASCII text, a match that fails, replacement references. | probe: duckdb-R15-regexp | probe: polars-regex | missing | fuzz: spark-regex |
| digests | md5, sha256 and other digests: present, and on which bytes. |
probe: duckdb-R15-digests | probe: polars-string-functions-missing | missing | probe: spark-digests |
temporal
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
date_range |
Date and timestamp arithmetic past 0001-01-01 … 9999-12-31. | probe: duckdb-date-range | probe: polars-date-range, polars-month-arith | missing | fuzz: spark-date-range |
time_interval_months |
TIME ± INTERVAL with months, and time arithmetic past midnight. |
probe: duckdb-time-plus-interval | probe: polars-temporal-errors | missing | probe: spark-time-type |
timestamp_precision |
The timestamp unit (s, ms, µs, ns) and its range. | engine_test: duckdb-R10 | probe: polars-timestamp-unit | missing | probe: spark-timestamp-precision |
time_zones |
Time zones: DST gaps and overlaps, conversions. | probe: duckdb-time-zones | probe: polars-temporal-errors | missing | probe: spark-time-zones |
week_numbering |
Week numbers, weekdays, ISO years, date_trunc to a week, parts before 1970. | probe: duckdb-yearweek | probe: polars-date-parts | missing | probe: spark-week-parts |
casts
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
| float_to_int | A double or decimal cast to an integer: truncate or round. | probe: duckdb-R9 | probe: polars-cast-double-to-int, polars-decimal-casts | missing | probe: spark-cast-double-to-int |
cast_failure |
A cast that does not fit or parse: raise or NULL. | engine_test: duckdb-R8 | probe: polars-cast-double-to-int, polars-cast-int, polars-cast-string, polars-decimal-casts | missing | fuzz: spark-cast-double-to-int, spark-cast-string |
cast_parsing |
Text to numbers, booleans and dates: what is accepted (spaces, signs, nan, inf, exponents). |
engine_test: duckdb-R10 | probe: polars-cast-string, polars-temporal-errors | missing | fuzz: spark-cast-string |
cast_formatting |
Numbers, booleans and dates to text: the format. | probe: duckdb-printf | probe: polars-cast-string, polars-strftime | missing | fuzz: spark-cast-string |
nulls
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
null_handling |
concat and other functions given a NULL: NULL or skip. |
probe: duckdb-null-concat | probe: polars-concat | missing | probe: spark-concat |
three_valued_logic |
and, or, not with NULLs; bool_and, bool_or over NULLs. |
probe: duckdb-kleene | probe: polars-kleene | missing | probe: spark-kleene |
null_join_keys |
Do NULL keys match in joins, and how IS NOT DISTINCT FROM is spelled. |
probe: duckdb-null-keys, duckdb-set-operations | probe: polars-null-keys, polars-set-operations | missing | probe: spark-null-keys, spark-set-operations |
nested
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
list_null_order |
A NULL element deciding < of lists, in comparisons and in ORDER BY. |
engine_test: duckdb-R11 | probe: polars-list-order | missing | fuzz: spark-list-order |
list_indexing |
List indexing: base, negative and out-of-range indexes. | probe: duckdb-list-index | probe: polars-list-index | missing | fuzz: spark-list-index |
struct_equality |
Equality and order of structs with NULL fields. | probe: duckdb-struct-equality | probe: polars-nested-equality | missing | probe: spark-nested-equality |
unnest_null_empty |
UNNEST of a NULL list and of an empty one. | probe: duckdb-unnest | probe: polars-explode | missing | probe: spark-explode |
aggregates
windows
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
ntile_spread |
How NTILE spreads the remainder. |
probe: duckdb-ntile | probe: polars-rank | missing | probe: spark-rank |
default_window_frame |
The frame when none is given, with and without an order; rank functions over ties and NULLs. | probe: duckdb-window-frame | probe: polars-rank, polars-window-values | missing | probe: spark-rank, spark-window-values |
relations
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
tie_order |
The order of ties in a sort, STRING_AGG, collect, a window: stable or not. |
probe: duckdb-mode-ties, duckdb-sort-ties | probe: polars-first-last, polars-sort-stability-streaming, polars-rank, polars-window-values | missing | probe: spark-first-last, spark-sort-stability, spark-rank, spark-window-values |
null_order |
Where NULLs sort by default, ascending and descending. | probe: duckdb-F2, duckdb-window-frame | probe: polars-null-order | missing | probe: spark-null-order |
clustered |
A sort that only clusters equal keys. | probe: duckdb-clustered | probe: polars-clustered | missing | probe: spark-clustered |
set_operations |
UNION, INTERSECT, EXCEPT, with and without ALL: duplicates, NULLs, order. |
probe: duckdb-set-operations | probe: polars-set-operations | missing | probe: spark-set-operations |
nan_zero_grouping |
Grouping, DISTINCT and join keys with NaN and -0.0: one group, and which value represents it. |
probe: duckdb-R14, duckdb-nan-equality | probe: polars-nan-equality, polars-zero-order | missing | fuzz: spark-nan-equality, spark-zero-order |
errors
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
raise_set |
Where the engine raises beyond the reference (D2): extra raises, and raises that are planning errors or panics. | probe: duckdb-moments, duckdb-decimal-arith, duckdb-R15-int-modulus, duckdb-R15-shift-left, duckdb-list-vectors, duckdb-printf, duckdb-date-range, duckdb-ntile | probe: polars-decimal-arith, polars-list-index, polars-date-range | missing | fuzz: spark-decimal-negate, spark-raise-set, spark-list-index, spark-regex, spark-substring-range, spark-date-range |
execution
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
evaluation_order |
Short-circuit in CASE, AND, OR, COALESCE: does the engine evaluate an expression on rows a filter or a branch removes, and raise there (division by zero, a failing cast)? This can break the reference’s no-error guarantee. |
fuzz: duckdb-exec-filter-pushdown, duckdb-exec-short-circuit | probe: polars-exec-cse-hoist, polars-exec-predicate-pushdown, polars-exec-when-eager | missing | probe: spark-exec-evaluation-order |
settings |
Which session settings change results: DuckDB’s default_null_order, integer_division, threads, …; Polars’s Config and environment variables. |
probe: duckdb-exec-settings | engine_test: polars-exec-engine | missing | fuzz: spark-exec-parser-cache, spark-exec-settings, spark-list-index, spark-case, spark-time-zones |
parallel_nondeterminism |
Results that change with the thread count or from run to run: the order of a double SUM, row order without ORDER BY, ties. |
probe: duckdb-exec-row-order | probe: polars-exec-row-order | missing | fuzz: spark-first-last, spark-sum-double, spark-sum-int, spark-exec-parallel, spark-sort-stability, spark-window-values |
platform |
Results that depend on the platform: libm (sin, pow, exp), NaN signs, SIMD paths; probes ran on macOS arm64, CI on Linux x86_64. |
probe: duckdb-exec-platform | probe: polars-exec-platform | missing | fuzz: spark-elem-rounding, spark-nan-sign, spark-exec-platform |
size_limits |
Limits on string, blob and list lengths, decimal precision, nesting depth, and what happens past them. | engine_test: duckdb-exec-size-limits | engine_test: polars-exec-size-limits | missing | fuzz: spark-exec-parser-cache, spark-exec-size-limits |
streaming_fallback |
Operations a streaming engine cannot run itself and hands to its in-memory engine (Polars’s streaming engine, which sverify targets): which, and whether results differ. | missing | engine_test: polars-exec-streaming-fallback | missing | source: spark-exec-streaming-fallback |
try_variants |
TRY_CAST, TRY(…) and other non-raising forms: which errors become NULL and which still raise. |
engine_test: duckdb-exec-try | engine_test: polars-exec-strict-false | missing | fuzz: spark-try-variants |
frontend
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
type_names |
The engine’s names of the types. | probe: duckdb-typeof-names, duckdb-F4 | probe: polars-type-names | missing | probe: spark-type-names |
implicit_casts |
Implicit casts and type promotion in arithmetic and comparisons. | engine_test: duckdb-F1 | probe: polars-fp32, polars-int-float-compare, polars-implicit-casts, polars-int-true-divide | missing | fuzz: spark-fp32, spark-implicit-casts |
literal_types |
The types of literals. | engine_test: duckdb-F3 | probe: polars-literal-types | engine_test: datafusion-S4 | probe: spark-literal-types |
io
| Item | Question | DuckDB | Polars | DataFusion | Spark |
|---|---|---|---|---|---|
io_formats |
File formats read and written, and their type mappings. | probe: duckdb-IO3 | probe: polars-io | missing | engine_test: spark-io |
object_stores |
Object stores and their credentials. | probe: duckdb-IO1, duckdb-IO2, duckdb-IO3 | probe: polars-io | missing | engine_test: spark-io |
catalogs |
Catalogs (Unity, Iceberg, …) and how the engine attaches them. | probe: duckdb-IO1, duckdb-IO2 | probe: polars-io | missing | engine_test: spark-io |