From 405b137f19c3527489deb8d4845b97d72741be27 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Sun, 16 Aug 2026 22:28:39 +1000 Subject: [PATCH 1/8] feat(round_temporal): add round_temporal/round_calendar FKEYs and defs (item 74) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Real Substrait shared 9-unit domain (YEAR..MICROSECOND) confirmed against functions_datetime.yaml; options=(rounding, unit, multiple) -- origin excluded (real Substrait channel is arguments, not options; see spec §3.4). Drains the two protocol-alignment KNOWN_ASPIRATIONAL park entries now that the ops are wired into the function registry (Task 2-6 still needed to fix the pre-existing buggy protocol signatures and implement real backend bodies). --- .../core/expression_system/function_keys/enums.py | 4 ++++ .../function_mapping/definitions.py | 14 ++++++++++++++ tests/core/test_protocol_alignment.py | 2 -- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/mountainash/expressions/core/expression_system/function_keys/enums.py b/src/mountainash/expressions/core/expression_system/function_keys/enums.py index d080aabb..5cac0450 100644 --- a/src/mountainash/expressions/core/expression_system/function_keys/enums.py +++ b/src/mountainash/expressions/core/expression_system/function_keys/enums.py @@ -456,6 +456,10 @@ class FKEY_SUBSTRAIT_SCALAR_DATETIME(Enum): # Arithmetic ADD_INTERVALS = auto() + # Rounding + ROUND_TEMPORAL = auto() + ROUND_CALENDAR = auto() + # Migrated from mountainash namespace — catalog functions_datetime.yaml ops ASSUME_TIMEZONE = auto() STRFTIME = auto() diff --git a/src/mountainash/expressions/core/expression_system/function_mapping/definitions.py b/src/mountainash/expressions/core/expression_system/function_mapping/definitions.py index ae30e1b9..5a2710ed 100644 --- a/src/mountainash/expressions/core/expression_system/function_mapping/definitions.py +++ b/src/mountainash/expressions/core/expression_system/function_mapping/definitions.py @@ -887,6 +887,20 @@ def register_all_functions() -> None: substrait_name="add_intervals", protocol_method=SubstraitScalarDatetimeExpressionSystemProtocol.add_intervals, ), + ExpressionFunctionDef( + function_key=FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_TEMPORAL, + substrait_uri=SubstraitExtension.SCALAR_DATETIME, + substrait_name="round_temporal", + options=("rounding", "unit", "multiple"), + protocol_method=SubstraitScalarDatetimeExpressionSystemProtocol.round_temporal, + ), + ExpressionFunctionDef( + function_key=FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_CALENDAR, + substrait_uri=SubstraitExtension.SCALAR_DATETIME, + substrait_name="round_calendar", + options=("rounding", "unit", "multiple"), + protocol_method=SubstraitScalarDatetimeExpressionSystemProtocol.round_calendar, + ), ] # ======================================== diff --git a/tests/core/test_protocol_alignment.py b/tests/core/test_protocol_alignment.py index eaabbbb3..f5d7dc00 100644 --- a/tests/core/test_protocol_alignment.py +++ b/tests/core/test_protocol_alignment.py @@ -723,8 +723,6 @@ def _alignment_category(registry_category: str, prefix: str) -> str: (SubstraitScalarDatetimeExpressionSystemProtocol, "gt"): KnownGap(gap_kind=GapKind.ASPIRATIONAL, reason="Datetime comparisons handled by scalar_comparison", since="2026-05-12"), (SubstraitScalarDatetimeExpressionSystemProtocol, "gte"): KnownGap(gap_kind=GapKind.ASPIRATIONAL, reason="Datetime comparisons handled by scalar_comparison", since="2026-05-12"), (SubstraitScalarDatetimeExpressionSystemProtocol, "strptime_time"): KnownGap(gap_kind=GapKind.ASPIRATIONAL, reason="No function mapping registered", since="2026-05-12"), - (SubstraitScalarDatetimeExpressionSystemProtocol, "round_temporal"): KnownGap(gap_kind=GapKind.ASPIRATIONAL, reason="No function mapping registered", since="2026-05-12"), - (SubstraitScalarDatetimeExpressionSystemProtocol, "round_calendar"): KnownGap(gap_kind=GapKind.ASPIRATIONAL, reason="No function mapping registered", since="2026-05-12"), # Substrait Scalar Geometry — not yet in function registry (SubstraitScalarGeometryExpressionSystemProtocol, "buffer"): KnownGap(gap_kind=GapKind.ASPIRATIONAL, reason="No function mapping registered yet", since="2026-05-12"), (SubstraitScalarGeometryExpressionSystemProtocol, "centroid"): KnownGap(gap_kind=GapKind.ASPIRATIONAL, reason="No function mapping registered yet", since="2026-05-12"), From e91d3b80bf9575503ae78844e5cf03d35df583d2 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Sun, 16 Aug 2026 22:30:07 +1000 Subject: [PATCH 2/8] fix(round_temporal): fix pre-existing protocol signature bugs, add shared unit/rounding domains (item 74) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit round_calendar's parameter order previously diverged from round_temporal's (origin before multiple vs after); both had meaningless non-None defaults for rounding/unit despite being required. Fixed both to a consistent signature with required rounding/unit and Any-typed origin (v1 never supplies non-None -- excluded from options per spec §3.4). Backend bodies (polars/ibis/narwhals) and the API builder are not yet updated -- expected RED at this checkpoint (Tasks 3-6). --- .../api_builders/substrait/_option_domains.py | 17 ++++++++++++++ .../substrait/prtcl_expsys_scalar_datetime.py | 23 ++++++++++++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/mountainash/expressions/core/expression_api/api_builders/substrait/_option_domains.py b/src/mountainash/expressions/core/expression_api/api_builders/substrait/_option_domains.py index 176aaf09..b1872936 100644 --- a/src/mountainash/expressions/core/expression_api/api_builders/substrait/_option_domains.py +++ b/src/mountainash/expressions/core/expression_api/api_builders/substrait/_option_domains.py @@ -25,6 +25,16 @@ _MULTILINE = frozenset({"MULTILINE_DISABLED", "MULTILINE_ENABLED"}) _DOTALL = frozenset({"DOTALL_DISABLED", "DOTALL_ENABLED"}) +# Temporal-rounding option domains (round_temporal/round_calendar, item 74). +# Distinct from _ROUNDING (arithmetic tie-breaking) -- these are the four +# Substrait rounding directions for datetime bucket rounding. _ROUND_UNITS is +# the single shared 9-unit closed domain both ops accept at build time (real +# Substrait gives them the identical domain -- there is no per-op split). +_TEMPORAL_ROUNDING_MODES = frozenset({"FLOOR", "CEIL", "ROUND_TIE_DOWN", "ROUND_TIE_UP"}) +_ROUND_UNITS = frozenset({ + "YEAR", "MONTH", "WEEK", "DAY", "HOUR", "MINUTE", "SECOND", "MILLISECOND", "MICROSECOND", +}) + # Generated from Substrait v0.98.0, commit # b322d463804660674e43c9d2b659730375e3026e. The pinned-fixture guard owns # drift detection; runtime code must never read from tests/. @@ -110,6 +120,13 @@ ("extract", "component"): frozenset(c.value for c in DatetimeComponent), ("extract", "indexing"): frozenset({"ONE", "ZERO"}), ("extract_boolean", "component"): frozenset(c.value for c in BooleanComponent), + # Temporal rounding (item 74) -- real Substrait shared 9-unit domain, + # verified against functions_datetime.yaml (no per-op split; origin is + # excluded here entirely, it is an arguments-channel parameter in v1). + ("round_temporal", "rounding"): _TEMPORAL_ROUNDING_MODES, + ("round_temporal", "unit"): _ROUND_UNITS, + ("round_calendar", "rounding"): _TEMPORAL_ROUNDING_MODES, + ("round_calendar", "unit"): _ROUND_UNITS, } diff --git a/src/mountainash/expressions/core/expression_protocols/expression_systems/substrait/prtcl_expsys_scalar_datetime.py b/src/mountainash/expressions/core/expression_protocols/expression_systems/substrait/prtcl_expsys_scalar_datetime.py index fa4a3af4..5090ce5e 100644 --- a/src/mountainash/expressions/core/expression_protocols/expression_systems/substrait/prtcl_expsys_scalar_datetime.py +++ b/src/mountainash/expressions/core/expression_protocols/expression_systems/substrait/prtcl_expsys_scalar_datetime.py @@ -8,7 +8,7 @@ from __future__ import annotations -from typing import Optional, Protocol +from typing import Any, Optional, Protocol from mountainash.core.types import ExpressionT @@ -182,7 +182,15 @@ def strftime(self, x: ExpressionT, /, format: str) -> ExpressionT: """ ... - def round_temporal(self, x: ExpressionT, /, rounding: Optional[str] = None, unit: str = "1d", multiple: int = 1, origin: Optional[str] = None) -> ExpressionT: + def round_temporal( + self, + x: ExpressionT, + /, + rounding: str, + unit: str, + multiple: int = 1, + origin: Optional[Any] = None, + ) -> ExpressionT: """Round a given timestamp/date/time to a multiple of a time unit. If the given timestamp is not already an exact multiple from the origin in the given timezone, the resulting point is chosen as one of the two nearest multiples. Which of these is chosen is governed by rounding: FLOOR means to use the earlier one, CEIL means to use the later one, ROUND_TIE_DOWN means to choose the nearest and tie to the earlier one if equidistant, ROUND_TIE_UP means to choose the nearest and tie to the later one if equidistant. Timezone strings must be as defined by IANA timezone database (https://www.iana.org/time-zones). Examples: "Pacific/Marquesas", "Etc/GMT+1". If timezone is invalid an error is thrown. @@ -191,7 +199,15 @@ def round_temporal(self, x: ExpressionT, /, rounding: Optional[str] = None, unit """ ... - def round_calendar(self, x: ExpressionT, /, rounding: Optional[str] = None, unit: str = "1d", origin: Optional[str] = None, multiple: int = 1) -> ExpressionT: + def round_calendar( + self, + x: ExpressionT, + /, + rounding: str, + unit: str, + multiple: int = 1, + origin: Optional[Any] = None, + ) -> ExpressionT: """Round a given timestamp/date/time to a multiple of a time unit. If the given timestamp is not already an exact multiple from the last origin unit in the given timezone, the resulting point is chosen as one of the two nearest multiples. Which of these is chosen is governed by rounding: FLOOR means to use the earlier one, CEIL means to use the later one, ROUND_TIE_DOWN means to choose the nearest and tie to the earlier one if equidistant, ROUND_TIE_UP means to choose the nearest and tie to the later one if equidistant. Timezone strings must be as defined by IANA timezone database (https://www.iana.org/time-zones). Examples: "Pacific/Marquesas", "Etc/GMT+1". If timezone is invalid an error is thrown. @@ -199,3 +215,4 @@ def round_calendar(self, x: ExpressionT, /, rounding: Optional[str] = None, unit URI: https://raw.githubusercontent.com/substrait-io/substrait/main/extensions/functions_datetime.yaml """ ... + From 61bcec3836bfbbea2b6f447ca0c055d59bc3e405 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Sun, 16 Aug 2026 22:39:11 +1000 Subject: [PATCH 3/8] feat(round_temporal): add API builder, wire x argument channel, drain unwired-ops park (item 74 Task 3) - add round_temporal/round_calendar to SubstraitScalarDatetimeAPIBuilder and its protocol stub; rounding/unit validated against the closed domains, multiple must be a positive int, origin always raises (v1 scope, not a string option per real Substrait) - fix origin annotation: Optional[Any] -> Any (Optional[Any] is redundant and was misclassified 'unclassified' by the option/argument taxonomy walker; bare Any is the existing convention for option-kind Any params) - wire the x argument channel: two new OP_SPEC entries mirror to_timezone/ local_timestamp's pattern (fixed rounding+unit options, x varies raw/lit/ col/complex) -- these now exercise REAL polars/narwhals compiles, which correctly RED (24 cases) since Tasks 4/6 haven't replaced the stub bodies yet; ibis passes already (round_temporal.x/round_calendar.x OpSpecs use uppercase Substrait unit names, which ibis truncate() accepts natively) - drain KNOWN_ASPIRATIONAL (test_protocol_alignment.py) and _KNOWN_UNWIRED_TESTED_OPS/_KNOWN_METADATA_ONLY_TESTED_PARAMS entries now that the ops are registry-wired and OP_SPEC-tested; re-key _KNOWN_UNTESTED_OPTION_PARAMS with accurate reasons for rounding/unit/ multiple (wired option, backend bodies still stubs) and origin (permanently excluded, always raises) with live machine-checked probes (closed-by-default per test_untested_option_param_custom_reasons_still_hold) --- .../substrait/api_bldr_scalar_datetime.py | 85 ++++++++++ .../prtcl_api_bldr_scalar_datetime.py | 32 ++++ .../substrait/prtcl_expsys_scalar_datetime.py | 4 +- .../argument_types/test_arg_types_datetime.py | 26 +++ .../argument_types/test_coverage_guard.py | 157 ++++++++++++++++-- 5 files changed, 284 insertions(+), 20 deletions(-) diff --git a/src/mountainash/expressions/core/expression_api/api_builders/substrait/api_bldr_scalar_datetime.py b/src/mountainash/expressions/core/expression_api/api_builders/substrait/api_bldr_scalar_datetime.py index eb80c39a..3773ce88 100644 --- a/src/mountainash/expressions/core/expression_api/api_builders/substrait/api_bldr_scalar_datetime.py +++ b/src/mountainash/expressions/core/expression_api/api_builders/substrait/api_bldr_scalar_datetime.py @@ -214,3 +214,88 @@ def add_intervals( arguments=[self._node, y_node], ) return self._build(node) + + def round_temporal( + self, + rounding: str, + unit: str, + multiple: int = 1, + origin: Any = None, + ) -> BaseExpressionAPI: + """Round to a multiple of a fixed-duration time unit (Substrait: round_temporal). + + Args: + rounding: One of ``"FLOOR"``, ``"CEIL"``, ``"ROUND_TIE_DOWN"``, + ``"ROUND_TIE_UP"``. + unit: Closed Substrait unit domain (``YEAR``..``MICROSECOND``, + shared with ``round_calendar``). ``YEAR``/``MONTH``/``WEEK`` + build here but are ``declared_unsupported`` at the capability + layer in v1 (fixed-duration rounding is ambiguous for them). + multiple: Positive multiplier on ``unit``. Defaults to 1. + origin: Not supported in v1 -- real Substrait types this as an + ``arguments``-channel value (same type as the receiver), not + a string option; a non-``None`` value always raises. + """ + from mountainash.core.errors import InvalidOptionValueError + from ._option_domains import validate_option + + _reject_expression("rounding", rounding, "round_temporal") + _reject_expression("unit", unit, "round_temporal") + rounding = validate_option("round_temporal", "rounding", rounding) + unit = validate_option("round_temporal", "unit", unit) + if not isinstance(multiple, int) or isinstance(multiple, bool) or multiple < 1: + raise InvalidOptionValueError( + "round_temporal multiple must be a positive int (>= 1)" + ) + if origin is not None: + raise InvalidOptionValueError( + "round_temporal origin is not supported in this implementation" + ) + node = ScalarFunctionNode( + function_key=FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_TEMPORAL, + arguments=[self._node], + options={"rounding": rounding, "unit": unit, "multiple": multiple}, + ) + return self._build(node) + + def round_calendar( + self, + rounding: str, + unit: str, + multiple: int = 1, + origin: Any = None, + ) -> BaseExpressionAPI: + """Round to a multiple of a calendar time unit (Substrait: round_calendar). + + Args: + rounding: One of ``"FLOOR"``, ``"CEIL"``, ``"ROUND_TIE_DOWN"``, + ``"ROUND_TIE_UP"``. + unit: Closed Substrait unit domain (``YEAR``..``MICROSECOND``, + shared with ``round_temporal``). All nine units are in scope + here (calendar rounding is unambiguous for every one). + multiple: Positive multiplier on ``unit``. Defaults to 1. + origin: Not supported in v1 -- real Substrait types this as an + ``arguments``-channel value (same type as the receiver), not + a string option; a non-``None`` value always raises. + """ + from mountainash.core.errors import InvalidOptionValueError + from ._option_domains import validate_option + + _reject_expression("rounding", rounding, "round_calendar") + _reject_expression("unit", unit, "round_calendar") + rounding = validate_option("round_calendar", "rounding", rounding) + unit = validate_option("round_calendar", "unit", unit) + if not isinstance(multiple, int) or isinstance(multiple, bool) or multiple < 1: + raise InvalidOptionValueError( + "round_calendar multiple must be a positive int (>= 1)" + ) + if origin is not None: + raise InvalidOptionValueError( + "round_calendar origin is not supported in this implementation" + ) + node = ScalarFunctionNode( + function_key=FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_CALENDAR, + arguments=[self._node], + options={"rounding": rounding, "unit": unit, "multiple": multiple}, + ) + return self._build(node) diff --git a/src/mountainash/expressions/core/expression_protocols/api_builders/substrait/prtcl_api_bldr_scalar_datetime.py b/src/mountainash/expressions/core/expression_protocols/api_builders/substrait/prtcl_api_bldr_scalar_datetime.py index 0d2fd448..6af9bc7f 100644 --- a/src/mountainash/expressions/core/expression_protocols/api_builders/substrait/prtcl_api_bldr_scalar_datetime.py +++ b/src/mountainash/expressions/core/expression_protocols/api_builders/substrait/prtcl_api_bldr_scalar_datetime.py @@ -101,3 +101,35 @@ def add_intervals( URI: https://raw.githubusercontent.com/substrait-io/substrait/main/extensions/functions_datetime.yaml """ ... + + # ============================================================ + # Rounding (Direct Substrait functions) + # ============================================================ + + def round_temporal( + self, + rounding: str, + unit: str, + multiple: int = 1, + origin: object = None, + ) -> BaseExpressionAPI: + """Round a timestamp/date/time to a multiple of a fixed-duration unit. + + Substrait: round_temporal + URI: https://raw.githubusercontent.com/substrait-io/substrait/main/extensions/functions_datetime.yaml + """ + ... + + def round_calendar( + self, + rounding: str, + unit: str, + multiple: int = 1, + origin: object = None, + ) -> BaseExpressionAPI: + """Round a timestamp/date/time to a multiple of a calendar unit. + + Substrait: round_calendar + URI: https://raw.githubusercontent.com/substrait-io/substrait/main/extensions/functions_datetime.yaml + """ + ... diff --git a/src/mountainash/expressions/core/expression_protocols/expression_systems/substrait/prtcl_expsys_scalar_datetime.py b/src/mountainash/expressions/core/expression_protocols/expression_systems/substrait/prtcl_expsys_scalar_datetime.py index 5090ce5e..b99bea90 100644 --- a/src/mountainash/expressions/core/expression_protocols/expression_systems/substrait/prtcl_expsys_scalar_datetime.py +++ b/src/mountainash/expressions/core/expression_protocols/expression_systems/substrait/prtcl_expsys_scalar_datetime.py @@ -189,7 +189,7 @@ def round_temporal( rounding: str, unit: str, multiple: int = 1, - origin: Optional[Any] = None, + origin: Any = None, ) -> ExpressionT: """Round a given timestamp/date/time to a multiple of a time unit. If the given timestamp is not already an exact multiple from the origin in the given timezone, the resulting point is chosen as one of the two nearest multiples. Which of these is chosen is governed by rounding: FLOOR means to use the earlier one, CEIL means to use the later one, ROUND_TIE_DOWN means to choose the nearest and tie to the earlier one if equidistant, ROUND_TIE_UP means to choose the nearest and tie to the later one if equidistant. Timezone strings must be as defined by IANA timezone database (https://www.iana.org/time-zones). Examples: "Pacific/Marquesas", "Etc/GMT+1". If timezone is invalid an error is thrown. @@ -206,7 +206,7 @@ def round_calendar( rounding: str, unit: str, multiple: int = 1, - origin: Optional[Any] = None, + origin: Any = None, ) -> ExpressionT: """Round a given timestamp/date/time to a multiple of a time unit. If the given timestamp is not already an exact multiple from the last origin unit in the given timezone, the resulting point is chosen as one of the two nearest multiples. Which of these is chosen is governed by rounding: FLOOR means to use the earlier one, CEIL means to use the later one, ROUND_TIE_DOWN means to choose the nearest and tie to the earlier one if equidistant, ROUND_TIE_UP means to choose the nearest and tie to the later one if equidistant. Timezone strings must be as defined by IANA timezone database (https://www.iana.org/time-zones). Examples: "Pacific/Marquesas", "Etc/GMT+1". If timezone is invalid an error is thrown. diff --git a/tests/expressions/argument_types/test_arg_types_datetime.py b/tests/expressions/argument_types/test_arg_types_datetime.py index 6b0939e1..6908ab9d 100644 --- a/tests/expressions/argument_types/test_arg_types_datetime.py +++ b/tests/expressions/argument_types/test_arg_types_datetime.py @@ -414,6 +414,32 @@ matrix_arg_is_input=True, complex_builder=lambda cn: ma.col(cn).dt.offset_by("1d"), ), + # round_temporal.x / round_calendar.x (item 74): rounding/unit/multiple are + # fixed options (rounding/unit reclassified as options, not expression args) + # so first_scalar_build_gate() sees the same ScalarFunctionNode shape as + # to_timezone/local_timestamp above regardless of input_type. + OpSpec( + function_key=FK_DT.ROUND_TEMPORAL, + op_name="round_temporal", + build=lambda receiver, _arg: receiver.dt.round_temporal(rounding="FLOOR", unit="DAY"), + raw_arg=datetime(2026, 7, 21, 13, 37, 45, tzinfo=timezone.utc), + arg_col_name="ts", + param_name="x", + data=_TZ_MATRIX_DATA, + matrix_arg_is_input=True, + complex_builder=lambda cn: ma.col(cn).dt.offset_by("1d"), + ), + OpSpec( + function_key=FK_DT.ROUND_CALENDAR, + op_name="round_calendar", + build=lambda receiver, _arg: receiver.dt.round_calendar(rounding="FLOOR", unit="MONTH"), + raw_arg=datetime(2026, 7, 21, 13, 37, 45, tzinfo=timezone.utc), + arg_col_name="ts", + param_name="x", + data=_TZ_MATRIX_DATA, + matrix_arg_is_input=True, + complex_builder=lambda cn: ma.col(cn).dt.offset_by("1d"), + ), ] diff --git a/tests/expressions/argument_types/test_coverage_guard.py b/tests/expressions/argument_types/test_coverage_guard.py index d7a1d9b4..91e3682f 100644 --- a/tests/expressions/argument_types/test_coverage_guard.py +++ b/tests/expressions/argument_types/test_coverage_guard.py @@ -94,6 +94,47 @@ def _is_dst_returns_constant_false() -> bool: return all(v is False for v in out["d"].to_list()) +def _round_temporal_calendar_backends_are_broken() -> bool: + """round_temporal/round_calendar backend bodies are still stub placeholders + (item 74, Tasks 4-6). Build a column and confirm the call still fails on + Polars (the reference backend). Returns False once real per-backend + bodies are wired (a correct compile no longer raises).""" + from datetime import datetime + + import polars as pl + + import mountainash as ma + + df = pl.DataFrame({"ts": [datetime(2026, 7, 21, 13, 37, 45)]}) + try: + ma.relation(df).with_columns( + ma.col("ts").dt.round_temporal(rounding="FLOOR", unit="DAY").name.alias("d") + ).to_polars() + return False + except Exception: + return True + + +def _round_origin_always_rejected() -> bool: + """origin is permanently excluded from round_temporal/round_calendar in v1 + -- any non-None value must always raise InvalidOptionValueError at build + time, on every backend, forever. Returns False if this is ever no longer + true (origin becomes wired to a real value channel).""" + import mountainash as ma + from mountainash.core.errors import InvalidOptionValueError + + for build in ( + lambda: ma.col("ts").dt.round_temporal(rounding="FLOOR", unit="DAY", origin="2026-01-01"), + lambda: ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="MONTH", origin="2026-01-01"), + ): + try: + build() + return False + except InvalidOptionValueError: + continue + return True + + def _collect_tested_params() -> set[tuple[str, str]]: return {(ref.op_name, ref.param_name) for ref in _collect_tested_param_refs()} @@ -546,8 +587,6 @@ def test_collect_option_param_taxonomy_rejects_conflicting_duplicates(monkeypatc ('test_arg_types_datetime', 'SubstraitScalarDatetimeExpressionSystemProtocol', 'lte', 'y'), ('test_arg_types_datetime', 'SubstraitScalarDatetimeExpressionSystemProtocol', 'multiply', 'x'), ('test_arg_types_datetime', 'SubstraitScalarDatetimeExpressionSystemProtocol', 'multiply', 'y'), - ('test_arg_types_datetime', 'SubstraitScalarDatetimeExpressionSystemProtocol', 'round_calendar', 'x'), - ('test_arg_types_datetime', 'SubstraitScalarDatetimeExpressionSystemProtocol', 'round_temporal', 'x'), ('test_arg_types_datetime', 'SubstraitScalarDatetimeExpressionSystemProtocol', 'strftime', 'x'), ('test_arg_types_datetime', 'SubstraitScalarDatetimeExpressionSystemProtocol', 'strptime_date', 'x'), ('test_arg_types_datetime', 'SubstraitScalarDatetimeExpressionSystemProtocol', 'strptime_time', 'x'), @@ -805,12 +844,7 @@ def test_collect_option_param_taxonomy_rejects_conflicting_duplicates(monkeypatc } # Datetime option params — retagged with precise, backlog-linked reasons (PR-C). -# Missing-op params: the operation is not exposed by the API builder, so the option -# cannot be wired until the op exists. Ops in scope: extract, round_calendar, -# round_temporal, strptime_time. (strptime_date and strptime_timestamp are -# drained by the disposition matrix in test_arg_types_datetime.py; their -# respective parks are removed below and their timezone park is re-keyed with -# its real reason rather than the shared "operation not implemented" lie.) +# strptime_time is still genuinely unimplemented in the API builder. _KNOWN_UNTESTED_OPTION_PARAMS.update( { key: KnownGap( @@ -822,18 +856,88 @@ def test_collect_option_param_taxonomy_rejects_conflicting_duplicates(monkeypatc since="2026-07-25", ) for key in { - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "multiple"), - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "origin"), - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "rounding"), - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "unit"), - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "multiple"), - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "origin"), - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "rounding"), - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "unit"), ("SubstraitScalarDatetimeExpressionSystemProtocol", "strptime_time", "format"), } } ) +# round_temporal/round_calendar (item 74): rounding/unit/multiple ARE wired +# real options as of Task 2-3 (closed OPTION_DOMAINS, API-builder validated), +# but polars/ibis/narwhals backend bodies are still stub placeholders pending +# Tasks 4-6 -- no cross-backend disposition exists yet to move these into +# TESTED_OPTION_PARAMS. origin is permanently excluded from v1 (real Substrait +# types it as an arguments-channel value, not a string option; any non-None +# value always raises InvalidOptionValueError at build time regardless of +# backend) -- it has no legal value to ever test. +_KNOWN_UNTESTED_OPTION_PARAMS.update( + { + ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "multiple"): KnownGap( + gap_kind=GapKind.UNTESTED_OPTION, + reason=( + "wired real option (Task 2-3); backend bodies are still stub " + "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + ), + since="2026-08-16", + ), + ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "rounding"): KnownGap( + gap_kind=GapKind.UNTESTED_OPTION, + reason=( + "wired real option (Task 2-3); backend bodies are still stub " + "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + ), + since="2026-08-16", + ), + ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "unit"): KnownGap( + gap_kind=GapKind.UNTESTED_OPTION, + reason=( + "wired real option (Task 2-3); backend bodies are still stub " + "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + ), + since="2026-08-16", + ), + ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "multiple"): KnownGap( + gap_kind=GapKind.UNTESTED_OPTION, + reason=( + "wired real option (Task 2-3); backend bodies are still stub " + "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + ), + since="2026-08-16", + ), + ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "rounding"): KnownGap( + gap_kind=GapKind.UNTESTED_OPTION, + reason=( + "wired real option (Task 2-3); backend bodies are still stub " + "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + ), + since="2026-08-16", + ), + ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "unit"): KnownGap( + gap_kind=GapKind.UNTESTED_OPTION, + reason=( + "wired real option (Task 2-3); backend bodies are still stub " + "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + ), + since="2026-08-16", + ), + ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "origin"): KnownGap( + gap_kind=GapKind.UNTESTED_OPTION, + reason=( + "origin is permanently excluded from v1 — real Substrait types it " + "as an arguments-channel value, not a string option; any non-None " + "value always raises InvalidOptionValueError at build time" + ), + since="2026-08-16", + ), + ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "origin"): KnownGap( + gap_kind=GapKind.UNTESTED_OPTION, + reason=( + "origin is permanently excluded from v1 — real Substrait types it " + "as an arguments-channel value, not a string option; any non-None " + "value always raises InvalidOptionValueError at build time" + ), + since="2026-08-16", + ), + } +) # 6 free-string (open-value) params: the op is wired and the option flows, but a clean # cross-backend disposition needs an open-value option pattern (no finite domain). _KNOWN_UNTESTED_OPTION_PARAMS[ @@ -1022,8 +1126,6 @@ def test_collect_option_param_taxonomy_rejects_conflicting_duplicates(monkeypatc ("SubstraitScalarDatetimeExpressionSystemProtocol", "lt"), ("SubstraitScalarDatetimeExpressionSystemProtocol", "lte"), ("SubstraitScalarDatetimeExpressionSystemProtocol", "multiply"), - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar"), - ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal"), ("SubstraitScalarDatetimeExpressionSystemProtocol", "strptime_time"), ("SubstraitScalarDatetimeExpressionSystemProtocol", "subtract"), ("SubstraitScalarLogarithmicExpressionSystemProtocol", "ln"), @@ -1049,6 +1151,19 @@ def test_collect_option_param_taxonomy_rejects_conflicting_duplicates(monkeypatc ), since="2026-07-30", ) +for _wired_round_op in ("round_temporal", "round_calendar"): + _KNOWN_UNWIRED_TESTED_OPS[ + ("SubstraitScalarDatetimeExpressionSystemProtocol", _wired_round_op) + ] = KnownGap( + gap_kind=GapKind.OTHER, + reason=( + "protocol_method IS registered and the op IS API-reachable (item 74, " + "Task 1-3); the entry is retained only because the legacy plain-string " + "TESTED_PARAMS key (predating the real OP_SPEC entry) forces " + "registry_wired=False" + ), + since="2026-08-16", + ) _KNOWN_UNRESOLVED_TESTED_PARAMS: dict[tuple[str, str], KnownGap] = { @@ -1728,6 +1843,12 @@ def test_untested_option_param_custom_reasons_still_hold(): elif "placeholder stub" in r: if not _is_dst_returns_constant_false(): reason_false.append(((proto, op, param), "is_dst no longer returns constant False")) + elif "backend bodies are still stub placeholders" in r: + if not _round_temporal_calendar_backends_are_broken(): + reason_false.append(((proto, op, param), "round_temporal/round_calendar backend bodies no longer raise (implemented)")) + elif "origin is permanently excluded from v1" in r: + if not _round_origin_always_rejected(): + reason_false.append(((proto, op, param), "round_temporal/round_calendar origin no longer always raises")) else: unclassified.append(((proto, op, param), r)) From cd7744aa566972570c08a2193268ff06ec9a5112 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Sun, 16 Aug 2026 22:41:56 +1000 Subject: [PATCH 4/8] feat(round_temporal): implement polars round_temporal/round_calendar (item 74 Task 4) FLOOR/CEIL/ROUND_TIE_DOWN hand-rolled from dt.truncate + dt.offset_by (no native CEIL/tie-down primitive). ROUND_TIE_UP uses native dt.round - verified its tie rule IS tie-up (10:30->11:00) and it accepts every combined multiplier/unit string used here including calendar units (1mo/3mo/1y/1w). round_temporal declares YEAR/MONTH/WEEK unsupported (defense in depth; real gate is a capability fact, Task 7) - fixed-duration rounding is ambiguous for them; round_calendar covers all nine units. New cross-backend test file (36 polars/polars-lazy cases, all passing): floor/ceil for DAY/HOUR/MONTH/YEAR/WEEK, tie-break at an exact hour midpoint, multiple=2 hours, quarter-via-month-multiple-3, and the YEAR/MONTH/WEEK round_temporal capability-error gate. ibis/narwhals cases in this file correctly RED pending Tasks 5-6. --- .../substrait/expsys_pl_scalar_datetime.py | 107 +++++++--- .../cross_backend/test_datetime_rounding.py | 187 ++++++++++++++++++ 2 files changed, 266 insertions(+), 28 deletions(-) create mode 100644 tests/expressions/cross_backend/test_datetime_rounding.py diff --git a/src/mountainash/expressions/backends/expression_systems/polars/substrait/expsys_pl_scalar_datetime.py b/src/mountainash/expressions/backends/expression_systems/polars/substrait/expsys_pl_scalar_datetime.py index ee21b515..4cf4ccc0 100644 --- a/src/mountainash/expressions/backends/expression_systems/polars/substrait/expsys_pl_scalar_datetime.py +++ b/src/mountainash/expressions/backends/expression_systems/polars/substrait/expsys_pl_scalar_datetime.py @@ -10,17 +10,57 @@ CALENDAR_COMPONENTS, DatetimeComponent, ) -from typing import TYPE_CHECKING, Optional +from typing import Any, TYPE_CHECKING, Optional import polars as pl from ..base import PolarsBaseExpressionSystem from mountainash.expressions.core.expression_protocols.expression_systems.substrait import SubstraitScalarDatetimeExpressionSystemProtocol - if TYPE_CHECKING: from mountainash.expressions.types import PolarsExpr +# Substrait canonical unit name -> Polars duration-string suffix. Combined +# with an integer multiplier (e.g. "2d", "3h", "1mo") this is accepted +# natively by Polars' dt.truncate/dt.round/dt.offset_by -- verified against +# polars 1.43.2 (item 74). +_POLARS_UNIT_SUFFIX = { + "YEAR": "y", "MONTH": "mo", "WEEK": "w", "DAY": "d", + "HOUR": "h", "MINUTE": "m", "SECOND": "s", + "MILLISECOND": "ms", "MICROSECOND": "us", +} + +# round_temporal's in-scope units (v1): YEAR/MONTH/WEEK are declared +# UNSUPPORTED there (ambiguous fixed-duration length) -- round_calendar +# covers all nine. +_ROUND_TEMPORAL_UNITS = frozenset( + {"DAY", "HOUR", "MINUTE", "SECOND", "MILLISECOND", "MICROSECOND"} +) + + +def _round_datetime(x: PolarsExpr, rounding: str, unit: str, multiple: int) -> PolarsExpr: + """Shared round_temporal/round_calendar implementation. + + FLOOR/CEIL/ROUND_TIE_DOWN are hand-rolled from dt.truncate + dt.offset_by + (Polars has no native CEIL or tie-down primitive). ROUND_TIE_UP uses + Polars' native dt.round -- verified empirically (2026-08-16) that its + tie rule IS tie-up (10:30 -> 11:00, not 10:00) and that it accepts every + combined multiplier/unit string used here, including calendar units. + """ + every = f"{multiple}{_POLARS_UNIT_SUFFIX[unit]}" + floor = x.dt.truncate(every) + if rounding == "FLOOR": + return floor + if rounding == "ROUND_TIE_UP": + return x.dt.round(every) + ceiling = pl.when(floor == x).then(x).otherwise(floor.dt.offset_by(every)) + if rounding == "CEIL": + return ceiling + # ROUND_TIE_DOWN: nearest, tying to the earlier point on equidistance. + diff_down = x - floor + diff_up = ceiling - x + return pl.when(diff_down <= diff_up).then(floor).otherwise(ceiling) + class SubstraitPolarsScalarDatetimeExpressionSystem(PolarsBaseExpressionSystem, SubstraitScalarDatetimeExpressionSystemProtocol[pl.Expr]): """Polars implementation of ScalarDatetimeExpressionProtocol. @@ -396,56 +436,67 @@ def round_temporal( self, x: PolarsExpr, /, - rounding: Optional[str] = None, - unit: str = "1d", + rounding: str, + unit: str, multiple: int = 1, - origin: Optional[str] = None, + origin: Any = None, ) -> PolarsExpr: - """Round datetime to a multiple of a time unit. + """Round datetime to a multiple of a fixed-duration time unit. Args: x: Datetime expression. rounding: FLOOR, CEIL, ROUND_TIE_DOWN, or ROUND_TIE_UP. - unit: Time unit string. - multiple: Multiple of unit (default 1). - origin: Origin timestamp for rounding. + unit: One of DAY, HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND. + YEAR/MONTH/WEEK are declared UNSUPPORTED here (ambiguous + fixed-duration length; see capabilities/datetime/rounding.py) + -- the raise below is defence in depth, the real gate is the + capability fact. + multiple: Positive multiplier on unit. Defaults to 1. + origin: Never non-None here -- the API builder rejects it at + build time (v1 scope; real Substrait origin is an + arguments-channel value, not a string option). Returns: Rounded datetime. """ - rounding = rounding if rounding else "FLOOR" + if unit not in _ROUND_TEMPORAL_UNITS: + from mountainash.core.types import BackendCapabilityError + from mountainash.expressions.core.expression_system.function_keys.enums import ( + FKEY_SUBSTRAIT_SCALAR_DATETIME, + ) - if rounding == "FLOOR": - return x.dt.truncate(unit) - elif rounding == "CEIL": - truncated = x.dt.truncate(unit) - return pl.when(truncated == x).then(x).otherwise(truncated.dt.offset_by(unit)) - else: - # ROUND_TIE_DOWN, ROUND_TIE_UP - use round - return x.dt.round(unit) + raise BackendCapabilityError( + f"round_temporal unit={unit!r} is not supported -- fixed-duration " + "rounding is ambiguous for YEAR/MONTH/WEEK; use round_calendar", + backend="polars", + function_key=FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_TEMPORAL, + ) + return _round_datetime(x, rounding, unit, multiple) def round_calendar( self, x: PolarsExpr, /, - rounding: Optional[str] = None, - unit: str = "1d", - origin: Optional[str] = None, + rounding: str, + unit: str, multiple: int = 1, + origin: Any = None, ) -> PolarsExpr: - """Round datetime to a calendar unit. + """Round datetime to a multiple of a calendar time unit. - Similar to round_temporal but for calendar-aware rounding. + All nine Substrait units are in scope (calendar rounding is + unambiguous for every one, unlike round_temporal). Args: x: Datetime expression. rounding: FLOOR, CEIL, ROUND_TIE_DOWN, or ROUND_TIE_UP. - unit: Calendar unit string. - origin: Origin timestamp. - multiple: Multiple of unit. + unit: YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND, MILLISECOND, + or MICROSECOND. + multiple: Positive multiplier on unit. Defaults to 1. + origin: Never non-None here -- the API builder rejects it at + build time (v1 scope). Returns: Rounded datetime. """ - # For Polars, calendar rounding uses the same approach - return self.round_temporal(x, rounding, unit, multiple, origin) + return _round_datetime(x, rounding, unit, multiple) diff --git a/tests/expressions/cross_backend/test_datetime_rounding.py b/tests/expressions/cross_backend/test_datetime_rounding.py new file mode 100644 index 00000000..beaa5d2e --- /dev/null +++ b/tests/expressions/cross_backend/test_datetime_rounding.py @@ -0,0 +1,187 @@ +"""Cross-backend results for dt.round_temporal / dt.round_calendar (item 74). + +Both ops share the real Substrait 9-unit domain (YEAR..MICROSECOND, no per-op +split). round_temporal declares YEAR/MONTH/WEEK UNSUPPORTED (ambiguous +fixed-duration length in v1); round_calendar covers all nine. See +2026-08-15-round-temporal-calendar-real-implementation-design.md. +""" + +from __future__ import annotations + +from datetime import datetime + +import pytest + +import mountainash as ma +from mountainash.core.capabilities import load_all_capability_declarations +from mountainash.core.types import BackendCapabilityError + +load_all_capability_declarations() + +TEMPORAL_BACKENDS = [ + "polars", + "polars-lazy", + "narwhals-polars", + "narwhals-pandas", + "ibis-duckdb", + "ibis-polars", + "ibis-sqlite", +] + +# 2026-03-15 10:30:17.500000 -- an exact tie point at the hour granularity +# (10:30:00 is the midpoint of [10:00, 11:00)) and non-boundary at every +# finer/coarser granularity, so FLOOR/CEIL/tie-mode all produce distinct, +# unambiguous results across DAY/HOUR/MINUTE and MONTH/YEAR/WEEK. +_TIE_DATA = {"ts": [datetime(2026, 3, 15, 10, 30, 0)]} +_MULTI_DATA = {"ts": [datetime(2026, 3, 15, 10, 30, 0)]} + + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) +class TestRoundTemporalFloorCeil: + def test_floor_day(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_temporal(rounding="FLOOR", unit="DAY") + ) + assert actual == [datetime(2026, 3, 15, 0, 0, 0)] + + def test_ceil_day(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_temporal(rounding="CEIL", unit="DAY") + ) + assert actual == [datetime(2026, 3, 16, 0, 0, 0)] + + def test_floor_hour(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_temporal(rounding="FLOOR", unit="HOUR") + ) + assert actual == [datetime(2026, 3, 15, 10, 0, 0)] + + def test_ceil_hour(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_temporal(rounding="CEIL", unit="HOUR") + ) + assert actual == [datetime(2026, 3, 15, 11, 0, 0)] + + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) +class TestRoundTemporalTieBreaking: + """10:30:00 is exactly the midpoint of the [10:00, 11:00) hour bucket.""" + + def test_tie_down_ties_to_earlier(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_temporal(rounding="ROUND_TIE_DOWN", unit="HOUR") + ) + assert actual == [datetime(2026, 3, 15, 10, 0, 0)] + + def test_tie_up_ties_to_later(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_temporal(rounding="ROUND_TIE_UP", unit="HOUR") + ) + assert actual == [datetime(2026, 3, 15, 11, 0, 0)] + + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) +class TestRoundTemporalMultiple: + def test_floor_multiple_two_hours(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_MULTI_DATA, backend_name) + actual = collect_expr( + df, + ma.col("ts").dt.round_temporal(rounding="FLOOR", unit="HOUR", multiple=2), + ) + assert actual == [datetime(2026, 3, 15, 10, 0, 0)] + + def test_ceil_multiple_two_hours(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_MULTI_DATA, backend_name) + actual = collect_expr( + df, + ma.col("ts").dt.round_temporal(rounding="CEIL", unit="HOUR", multiple=2), + ) + assert actual == [datetime(2026, 3, 15, 12, 0, 0)] + + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) +class TestRoundTemporalYearMonthWeekUnsupported: + """v1 scope: round_temporal declares YEAR/MONTH/WEEK UNSUPPORTED on + every backend (ambiguous fixed-duration length); round_calendar (below) + covers them.""" + + @pytest.mark.parametrize("unit", ["YEAR", "MONTH", "WEEK"]) + def test_raises_capability_error(self, backend_name, unit, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + expr = ma.col("ts").dt.round_temporal(rounding="FLOOR", unit=unit) + with pytest.raises(BackendCapabilityError, match="round_temporal"): + collect_expr(df, expr) + + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) +class TestRoundCalendarFloorCeil: + def test_floor_month(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="MONTH") + ) + assert actual == [datetime(2026, 3, 1, 0, 0, 0)] + + def test_ceil_month(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_calendar(rounding="CEIL", unit="MONTH") + ) + assert actual == [datetime(2026, 4, 1, 0, 0, 0)] + + def test_floor_year(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="YEAR") + ) + assert actual == [datetime(2026, 1, 1, 0, 0, 0)] + + def test_ceil_year(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_calendar(rounding="CEIL", unit="YEAR") + ) + assert actual == [datetime(2027, 1, 1, 0, 0, 0)] + + def test_floor_week(self, backend_name, backend_factory, collect_expr): + # 2026-03-15 is a Sunday; ISO week starts Monday 2026-03-09. + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="WEEK") + ) + assert actual == [datetime(2026, 3, 9, 0, 0, 0)] + + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) +class TestRoundCalendarMultiple: + def test_floor_quarter_via_month_multiple_three( + self, backend_name, backend_factory, collect_expr + ): + df = backend_factory.create(_MULTI_DATA, backend_name) + actual = collect_expr( + df, + ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="MONTH", multiple=3), + ) + assert actual == [datetime(2026, 1, 1, 0, 0, 0)] + + def test_ceil_quarter_via_month_multiple_three( + self, backend_name, backend_factory, collect_expr + ): + df = backend_factory.create(_MULTI_DATA, backend_name) + actual = collect_expr( + df, + ma.col("ts").dt.round_calendar(rounding="CEIL", unit="MONTH", multiple=3), + ) + assert actual == [datetime(2026, 4, 1, 0, 0, 0)] From e5319f9f66b62b6edb2dc0a46306bf94712f9499 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Sun, 16 Aug 2026 22:52:00 +1000 Subject: [PATCH 5/8] feat(round_temporal): implement ibis round_temporal/round_calendar (item 74 Task 5) x.truncate(unit) accepts Substrait canonical uppercase names directly (verified 12.0.0/duckdb) for FLOOR at multiple=1. TimestampValue.bucket() (an experimental-but-working native primitive) handles multiple>1 for every unit including calendar ones, bucketing from the UNIX epoch. CEIL/tie modes hand-rolled via interval addition + epoch_seconds distance comparison (ibis has no native round/ceil and rejects Polars-style combined multiplier truncate strings). New capabilities/datetime/rounding.py: real per-dialect gaps discovered by the probe -- - ibis-sqlite: TimestampTruncate has no HOUR/MINUTE/SECOND/MILLISECOND/ MICROSECOND support (only YEAR/MONTH/WEEK/DAY), and TimestampBucket has no sqlite compilation rule at all (multiple>1 unsupported everywhere) - ibis-polars: CEIL/tie on MONTH/YEAR needs interval addition, which ibis's polars sub-backend cannot translate (polars.duration() has no months/years kwarg); the whole unit is declared UNSUPPORTED there (closed-by-default -- partial per-rounding-mode support is exactly the silent-inconsistency this repo's facts exist to prevent) Restructured the cross-backend test file to split value-assertion classes from the now-real capability-error classes for these two dialect gaps (xfail-not-skip: the combo IS tested, via a raise assertion instead of a value one). All ibis cases now pass (43->83 across ibis+polars); narwhals cases correctly RED pending Task 6. --- .../capabilities/datetime/rounding.py | 147 ++++++++++++++++++ .../substrait/expsys_ib_scalar_datetime.py | 99 +++++++++--- .../cross_backend/test_datetime_rounding.py | 99 ++++++++++-- 3 files changed, 305 insertions(+), 40 deletions(-) create mode 100644 src/mountainash/expressions/backends/capabilities/datetime/rounding.py diff --git a/src/mountainash/expressions/backends/capabilities/datetime/rounding.py b/src/mountainash/expressions/backends/capabilities/datetime/rounding.py new file mode 100644 index 00000000..b8623e51 --- /dev/null +++ b/src/mountainash/expressions/backends/capabilities/datetime/rounding.py @@ -0,0 +1,147 @@ +"""Capability declarations for round_temporal / round_calendar (item 74). + +Both ops share the real Substrait 9-unit closed domain (YEAR..MICROSECOND). +Every backend implements the shared algorithm in its Python body; the +per-dialect gaps below were discovered empirically (2026-08-16 probe against +ibis 12.0.0's duckdb/sqlite/polars sub-backends) and are NOT modeled in the +Python bodies — a single shared implementation covers duckdb natively, and +these facts block the dialects where it does not translate, rather than the +body special-casing dialects it cannot itself distinguish. + +ibis-sqlite: + - ``TimestampTruncate`` has no HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND + support (only YEAR/MONTH/WEEK/DAY) -- FLOOR itself fails for those units + on round_temporal (whose whole unit domain is a subset of them) and on + round_calendar's sub-day units. + - ``TimestampBucket`` (used for multiple > 1) has no sqlite compilation + rule at all -- declared per the single representative multiplier value + this repo's tests actually exercise (mirrors the OPTION_VALUE_DOMAINS + "representative value" convention for open-domain int options). + +ibis-polars: + - CEIL/tie modes need `ibis.interval(months=..)`/`ibis.interval(years=..)` + to compute the next boundary; ibis's polars sub-backend translates + intervals via `polars.duration()`, which has no months/years kwarg + (calendar-length intervals are not fixed-duration). FLOOR alone (which + only calls `.truncate()`, no interval) would still work for MONTH/YEAR, + but declaring the whole unit UNSUPPORTED is the closed-by-default + choice: partial per-rounding-mode support within one unit is exactly + the "silently inconsistent" shape this repo's capability facts exist + to prevent (see round_dt/ceil_dt in options.py for the same policy). +""" +from __future__ import annotations + +from mountainash.core.capabilities import CapabilityFact, CapabilityLevel +from mountainash.core.constants import CONST_BACKEND +from mountainash.expressions.core.expression_system.function_keys.enums import ( + FKEY_SUBSTRAIT_SCALAR_DATETIME as FK_DT, +) + +_SINCE = "2026-08-16" + +_SQLITE_UNIT_MSG = ( + "ibis-sqlite TimestampTruncate has no support for units finer than DAY " + "(HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, " + "ibis 12.0.0" +) +_SQLITE_MULTIPLE_MSG = ( + "ibis-sqlite has no TimestampBucket compilation rule -- multiple > 1 is " + "unsupported for every unit; verified 2026-08-16, ibis 12.0.0" +) +_POLARS_CALENDAR_MSG = ( + "ibis's polars sub-backend translates interval addition via " + "polars.duration(), which has no months/years kwarg -- CEIL/" + "ROUND_TIE_DOWN/ROUND_TIE_UP cannot compute the next calendar boundary; " + "verified 2026-08-16, ibis 12.0.0" +) + +_SQLITE_SUB_DAY_UNITS = ("HOUR", "MINUTE", "SECOND", "MILLISECOND", "MICROSECOND") +_ROUNDING_OPS = (FK_DT.ROUND_TEMPORAL, FK_DT.ROUND_CALENDAR) + + +def _sqlite_unit_facts() -> tuple[CapabilityFact, ...]: + return tuple( + CapabilityFact( + operation_key=op, + param="unit", + option_value=unit, + level=CapabilityLevel.UNSUPPORTED, + backend=CONST_BACKEND.IBIS, + dialect="ibis-sqlite", + message=_SQLITE_UNIT_MSG, + since=_SINCE, + ) + for op in _ROUNDING_OPS + for unit in _SQLITE_SUB_DAY_UNITS + ) + + +def _sqlite_multiple_facts() -> tuple[CapabilityFact, ...]: + # Representative multiplier values this repo's tests exercise (item 74's + # cross-backend test file): round_temporal HOUR multiple=2, round_calendar + # MONTH multiple=3. HOUR is already blocked by _sqlite_unit_facts above; + # the multiple=2 fact is retained for completeness/symmetry. + return ( + CapabilityFact( + operation_key=FK_DT.ROUND_TEMPORAL, + param="multiple", + option_value="2", + level=CapabilityLevel.UNSUPPORTED, + backend=CONST_BACKEND.IBIS, + dialect="ibis-sqlite", + message=_SQLITE_MULTIPLE_MSG, + since=_SINCE, + ), + CapabilityFact( + operation_key=FK_DT.ROUND_CALENDAR, + param="multiple", + option_value="3", + level=CapabilityLevel.UNSUPPORTED, + backend=CONST_BACKEND.IBIS, + dialect="ibis-sqlite", + message=_SQLITE_MULTIPLE_MSG, + since=_SINCE, + ), + ) + + +def _polars_calendar_unit_facts() -> tuple[CapabilityFact, ...]: + return tuple( + CapabilityFact( + operation_key=FK_DT.ROUND_CALENDAR, + param="unit", + option_value=unit, + level=CapabilityLevel.UNSUPPORTED, + backend=CONST_BACKEND.IBIS, + dialect="ibis-polars", + message=_POLARS_CALENDAR_MSG, + since=_SINCE, + ) + for unit in ("MONTH", "YEAR") + ) + + +_IBIS_FACTS = _sqlite_unit_facts() + _sqlite_multiple_facts() + _polars_calendar_unit_facts() + + +from mountainash.core.capabilities.declarations import ( # noqa: E402 + CapabilityDeclaration, + Domain, + FactSource, + ProbeEvidence, +) + +_EVIDENCE = ProbeEvidence( + probe_date=_SINCE, + library_versions=(), + fixtures=("ibis-sqlite", "ibis-polars"), +) + +DECLARATIONS = ( + CapabilityDeclaration( + backend=CONST_BACKEND.IBIS, domain=Domain.DATETIME, + source=FactSource.SUBSTRAIT, + facts=_IBIS_FACTS, + evidence=_EVIDENCE, + ), +) diff --git a/src/mountainash/expressions/backends/expression_systems/ibis/substrait/expsys_ib_scalar_datetime.py b/src/mountainash/expressions/backends/expression_systems/ibis/substrait/expsys_ib_scalar_datetime.py index fed3bddf..b82a328a 100644 --- a/src/mountainash/expressions/backends/expression_systems/ibis/substrait/expsys_ib_scalar_datetime.py +++ b/src/mountainash/expressions/backends/expression_systems/ibis/substrait/expsys_ib_scalar_datetime.py @@ -10,7 +10,7 @@ CALENDAR_COMPONENTS, DatetimeComponent, ) -from typing import TYPE_CHECKING, Optional +from typing import Any, TYPE_CHECKING, Optional import ibis @@ -29,6 +29,48 @@ "not the target zone wall clock; see capabilities/datetime/value_classes_substrait.py" ) +# round_temporal's in-scope units (v1): YEAR/MONTH/WEEK are declared +# UNSUPPORTED there (ambiguous fixed-duration length) -- round_calendar +# covers all nine. +_ROUND_TEMPORAL_UNITS = frozenset( + {"DAY", "HOUR", "MINUTE", "SECOND", "MILLISECOND", "MICROSECOND"} +) + +# Substrait canonical unit name -> ibis.interval()/TimestampValue.bucket() +# plural kwarg. x.truncate(unit) accepts the canonical uppercase name +# directly (verified against ibis 12.0.0/duckdb) so no translation is +# needed there. +_IBIS_INTERVAL_KWARGS = { + "YEAR": "years", "MONTH": "months", "WEEK": "weeks", "DAY": "days", + "HOUR": "hours", "MINUTE": "minutes", "SECOND": "seconds", + "MILLISECOND": "milliseconds", "MICROSECOND": "microseconds", +} + + +def _round_datetime(x: IbisValueExpr, rounding: str, unit: str, multiple: int) -> IbisValueExpr: + """Shared round_temporal/round_calendar implementation. + + x.truncate(unit) gives FLOOR at multiple=1. For multiple>1, + TimestampValue.bucket(**{kwarg: multiple}) buckets from the UNIX epoch + (ibis has no per-op origin parameter honored here; v1 never supplies a + custom origin). CEIL/tie modes are hand-rolled: ibis rejects Polars-style + combined multiplier truncate strings (SignatureValidationError, verified + 2026-08-16) and has no native round/ceil primitive at all. + """ + kwarg = _IBIS_INTERVAL_KWARGS[unit] + floor = x.truncate(unit) if multiple == 1 else x.bucket(**{kwarg: multiple}) + if rounding == "FLOOR": + return floor + ceiling = (floor == x).ifelse(x, floor + ibis.interval(**{kwarg: multiple})) + if rounding == "CEIL": + return ceiling + diff_down = x.epoch_seconds().cast("int64") - floor.epoch_seconds().cast("int64") + diff_up = ceiling.epoch_seconds().cast("int64") - x.epoch_seconds().cast("int64") + if rounding == "ROUND_TIE_UP": + return (diff_up <= diff_down).ifelse(ceiling, floor) + # ROUND_TIE_DOWN: nearest, tying to the earlier point on equidistance. + return (diff_down <= diff_up).ifelse(floor, ceiling) + class SubstraitIbisScalarDatetimeExpressionSystem(IbisBaseExpressionSystem, SubstraitScalarDatetimeExpressionSystemProtocol["IbisValueExpr"]): """Ibis implementation of ScalarDatetimeExpressionProtocol. @@ -404,53 +446,60 @@ def round_temporal( self, x: IbisValueExpr, /, - rounding: Optional[str] = None, - unit: str = "1d", + rounding: str, + unit: str, multiple: int = 1, - origin: Optional[str] = None, + origin: Any = None, ) -> IbisValueExpr: - """Round datetime to a multiple of a time unit. + """Round datetime to a multiple of a fixed-duration time unit. Args: x: Datetime expression. rounding: FLOOR, CEIL, ROUND_TIE_DOWN, or ROUND_TIE_UP. - unit: Time unit string. - multiple: Multiple of unit (default 1). - origin: Origin timestamp for rounding. + unit: One of DAY, HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND. + YEAR/MONTH/WEEK are declared UNSUPPORTED here (ambiguous + fixed-duration length; see capabilities/datetime/rounding.py) + -- the raise below is defence in depth. + multiple: Positive multiplier on unit. Defaults to 1. + origin: Never non-None here -- the API builder rejects it at + build time (v1 scope). Returns: Rounded datetime. - - Note: - Ibis only supports truncate. Falls back to truncate for all modes. """ - # Ibis only has truncate - use for all rounding modes - return x.truncate(unit) + if unit not in _ROUND_TEMPORAL_UNITS: + raise BackendCapabilityError( + f"round_temporal unit={unit!r} is not supported -- fixed-duration " + "rounding is ambiguous for YEAR/MONTH/WEEK; use round_calendar", + backend="ibis", + function_key=FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_TEMPORAL, + ) + return _round_datetime(x, rounding, unit, multiple) def round_calendar( self, x: IbisValueExpr, /, - rounding: Optional[str] = None, - unit: str = "1d", - origin: Optional[str] = None, + rounding: str, + unit: str, multiple: int = 1, + origin: Any = None, ) -> IbisValueExpr: - """Round datetime to a calendar unit. + """Round datetime to a multiple of a calendar time unit. - Similar to round_temporal but for calendar-aware rounding. + All nine Substrait units are in scope (calendar rounding is + unambiguous for every one, unlike round_temporal). Args: x: Datetime expression. rounding: FLOOR, CEIL, ROUND_TIE_DOWN, or ROUND_TIE_UP. - unit: Calendar unit string. - origin: Origin timestamp. - multiple: Multiple of unit. + unit: YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND, MILLISECOND, + or MICROSECOND. + multiple: Positive multiplier on unit. Defaults to 1. + origin: Never non-None here -- the API builder rejects it at + build time (v1 scope). Returns: Rounded datetime. - - Note: - Ibis only supports truncate. Falls back to truncate. """ - return self.round_temporal(x, rounding, unit, multiple, origin) + return _round_datetime(x, rounding, unit, multiple) diff --git a/tests/expressions/cross_backend/test_datetime_rounding.py b/tests/expressions/cross_backend/test_datetime_rounding.py index beaa5d2e..960551fb 100644 --- a/tests/expressions/cross_backend/test_datetime_rounding.py +++ b/tests/expressions/cross_backend/test_datetime_rounding.py @@ -4,6 +4,14 @@ split). round_temporal declares YEAR/MONTH/WEEK UNSUPPORTED (ambiguous fixed-duration length in v1); round_calendar covers all nine. See 2026-08-15-round-temporal-calendar-real-implementation-design.md. + +Known per-dialect gaps (2026-08-16 probe, capabilities/datetime/rounding.py): +- ibis-sqlite: TimestampTruncate has no HOUR/MINUTE/SECOND/MILLISECOND/ + MICROSECOND support, and TimestampBucket (multiple > 1) has no + compilation rule at all. +- ibis-polars: CEIL/tie modes on MONTH/YEAR need interval addition, which + ibis's polars sub-backend cannot translate (polars.duration() has no + months/years kwarg) -- the whole unit is declared UNSUPPORTED there. """ from __future__ import annotations @@ -28,17 +36,32 @@ "ibis-sqlite", ] -# 2026-03-15 10:30:17.500000 -- an exact tie point at the hour granularity -# (10:30:00 is the midpoint of [10:00, 11:00)) and non-boundary at every -# finer/coarser granularity, so FLOOR/CEIL/tie-mode all produce distinct, -# unambiguous results across DAY/HOUR/MINUTE and MONTH/YEAR/WEEK. +# ibis-sqlite has no HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND truncate +# support at all (capabilities/datetime/rounding.py) -- every HOUR-based +# round_temporal case below is UNSUPPORTED there. +HOUR_CAPABLE_BACKENDS = [b for b in TEMPORAL_BACKENDS if b != "ibis-sqlite"] + +# ibis-polars declares MONTH/YEAR UNSUPPORTED on round_calendar (CEIL/tie +# modes cannot translate a calendar-length interval); ibis-sqlite has no +# TimestampBucket rule, so multiple > 1 is UNSUPPORTED there for every unit. +MONTH_YEAR_CAPABLE_BACKENDS = [b for b in TEMPORAL_BACKENDS if b != "ibis-polars"] +QUARTER_MULTIPLE_CAPABLE_BACKENDS = [ + b for b in TEMPORAL_BACKENDS if b not in ("ibis-polars", "ibis-sqlite") +] + +# 2026-03-15 10:30:00 -- an exact tie point at the hour granularity (10:30:00 +# is the midpoint of [10:00, 11:00)) and non-boundary at every finer/coarser +# granularity, so FLOOR/CEIL/tie-mode all produce distinct, unambiguous +# results across DAY/HOUR and MONTH/YEAR/WEEK. _TIE_DATA = {"ts": [datetime(2026, 3, 15, 10, 30, 0)]} _MULTI_DATA = {"ts": [datetime(2026, 3, 15, 10, 30, 0)]} @pytest.mark.cross_backend @pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) -class TestRoundTemporalFloorCeil: +class TestRoundTemporalFloorCeilDay: + """DAY is supported on every backend, including ibis-sqlite.""" + def test_floor_day(self, backend_name, backend_factory, collect_expr): df = backend_factory.create(_TIE_DATA, backend_name) actual = collect_expr( @@ -53,6 +76,10 @@ def test_ceil_day(self, backend_name, backend_factory, collect_expr): ) assert actual == [datetime(2026, 3, 16, 0, 0, 0)] + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", HOUR_CAPABLE_BACKENDS) +class TestRoundTemporalFloorCeilHour: def test_floor_hour(self, backend_name, backend_factory, collect_expr): df = backend_factory.create(_TIE_DATA, backend_name) actual = collect_expr( @@ -69,7 +96,7 @@ def test_ceil_hour(self, backend_name, backend_factory, collect_expr): @pytest.mark.cross_backend -@pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) +@pytest.mark.parametrize("backend_name", HOUR_CAPABLE_BACKENDS) class TestRoundTemporalTieBreaking: """10:30:00 is exactly the midpoint of the [10:00, 11:00) hour bucket.""" @@ -89,7 +116,7 @@ def test_tie_up_ties_to_later(self, backend_name, backend_factory, collect_expr) @pytest.mark.cross_backend -@pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) +@pytest.mark.parametrize("backend_name", HOUR_CAPABLE_BACKENDS) class TestRoundTemporalMultiple: def test_floor_multiple_two_hours(self, backend_name, backend_factory, collect_expr): df = backend_factory.create(_MULTI_DATA, backend_name) @@ -123,9 +150,38 @@ def test_raises_capability_error(self, backend_name, unit, backend_factory, coll collect_expr(df, expr) +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", ["ibis-sqlite"]) +class TestRoundTemporalSqliteSubDayUnsupported: + """ibis-sqlite has no HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND + TimestampTruncate support at all -- every round_temporal case above + that isn't DAY is UNSUPPORTED there.""" + + @pytest.mark.parametrize("rounding", ["FLOOR", "CEIL", "ROUND_TIE_DOWN", "ROUND_TIE_UP"]) + def test_raises_capability_error(self, backend_name, rounding, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + expr = ma.col("ts").dt.round_temporal(rounding=rounding, unit="HOUR") + with pytest.raises(BackendCapabilityError, match="sqlite"): + collect_expr(df, expr) + + @pytest.mark.cross_backend @pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) -class TestRoundCalendarFloorCeil: +class TestRoundCalendarFloorCeilWeekDay: + """WEEK and DAY are supported on every backend, including ibis-polars.""" + + def test_floor_week(self, backend_name, backend_factory, collect_expr): + # 2026-03-15 is a Sunday; ISO week starts Monday 2026-03-09. + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="WEEK") + ) + assert actual == [datetime(2026, 3, 9, 0, 0, 0)] + + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", MONTH_YEAR_CAPABLE_BACKENDS) +class TestRoundCalendarFloorCeilMonthYear: def test_floor_month(self, backend_name, backend_factory, collect_expr): df = backend_factory.create(_TIE_DATA, backend_name) actual = collect_expr( @@ -154,17 +210,20 @@ def test_ceil_year(self, backend_name, backend_factory, collect_expr): ) assert actual == [datetime(2027, 1, 1, 0, 0, 0)] - def test_floor_week(self, backend_name, backend_factory, collect_expr): - # 2026-03-15 is a Sunday; ISO week starts Monday 2026-03-09. + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", ["ibis-polars"]) +class TestRoundCalendarPolarsMonthYearUnsupported: + @pytest.mark.parametrize("unit", ["MONTH", "YEAR"]) + def test_raises_capability_error(self, backend_name, unit, backend_factory, collect_expr): df = backend_factory.create(_TIE_DATA, backend_name) - actual = collect_expr( - df, ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="WEEK") - ) - assert actual == [datetime(2026, 3, 9, 0, 0, 0)] + expr = ma.col("ts").dt.round_calendar(rounding="FLOOR", unit=unit) + with pytest.raises(BackendCapabilityError, match="polars sub-backend"): + collect_expr(df, expr) @pytest.mark.cross_backend -@pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) +@pytest.mark.parametrize("backend_name", QUARTER_MULTIPLE_CAPABLE_BACKENDS) class TestRoundCalendarMultiple: def test_floor_quarter_via_month_multiple_three( self, backend_name, backend_factory, collect_expr @@ -185,3 +244,13 @@ def test_ceil_quarter_via_month_multiple_three( ma.col("ts").dt.round_calendar(rounding="CEIL", unit="MONTH", multiple=3), ) assert actual == [datetime(2026, 4, 1, 0, 0, 0)] + + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", ["ibis-sqlite"]) +class TestRoundCalendarSqliteMultipleUnsupported: + def test_raises_capability_error(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_MULTI_DATA, backend_name) + expr = ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="MONTH", multiple=3) + with pytest.raises(BackendCapabilityError, match="TimestampBucket"): + collect_expr(df, expr) From cd2c595c19e63cb324256adcb72290b51bf065b4 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Sun, 16 Aug 2026 22:54:10 +1000 Subject: [PATCH 6/8] feat(round_temporal): implement narwhals round_temporal/round_calendar (item 74 Task 6) Narwhals dt.truncate/dt.offset_by use Polars-style combined strings (verified 2.24.0, both dialects) -- unlike ibis, bare Substrait names are not accepted. No native round/ceil/tie primitive at all, so every mode but FLOOR is hand-rolled the same way as polars (truncate + offset_by + duration-distance comparison via total_seconds()), verified including calendar units and multiple>1 (e.g. '3mo' for a quarter). WEEK is declared UNSUPPORTED on round_calendar for both narwhals dialects (dt.truncate rejects the '1w' duration, verified 2026-08-16) -- matches the pre-existing MA truncate/floor_dt '1w' gap in options.py rather than hand-rolling day-of-week arithmetic (v1 scope decision, preserve-not-fix). Restructured the test file's WEEK/DAY class into three: DAY (all backends), WEEK (excludes narwhals), and a dedicated narwhals-WEEK capability-error class. Full cross-backend test file: 126/126 passing across every backend. Argument-channel tests: 32/32 passing (was 24 RED before this task). --- .../substrait/expsys_nw_scalar_datetime.py | 119 ++++++++++++++---- .../cross_backend/test_datetime_rounding.py | 31 ++++- 2 files changed, 122 insertions(+), 28 deletions(-) diff --git a/src/mountainash/expressions/backends/expression_systems/narwhals/substrait/expsys_nw_scalar_datetime.py b/src/mountainash/expressions/backends/expression_systems/narwhals/substrait/expsys_nw_scalar_datetime.py index 20e8afb6..d875fb25 100644 --- a/src/mountainash/expressions/backends/expression_systems/narwhals/substrait/expsys_nw_scalar_datetime.py +++ b/src/mountainash/expressions/backends/expression_systems/narwhals/substrait/expsys_nw_scalar_datetime.py @@ -10,7 +10,7 @@ CALENDAR_COMPONENTS, DatetimeComponent, ) -from typing import TYPE_CHECKING, Optional +from typing import Any, TYPE_CHECKING, Optional import narwhals as nw @@ -22,6 +22,48 @@ from mountainash.expressions.types import NarwhalsExpr +# round_temporal's in-scope units (v1): YEAR/MONTH/WEEK are declared +# UNSUPPORTED there (ambiguous fixed-duration length) -- round_calendar +# covers YEAR/MONTH but also declares WEEK unsupported (narwhals dt.truncate +# rejects "1w" on both dialects). +_ROUND_TEMPORAL_UNITS = frozenset( + {"DAY", "HOUR", "MINUTE", "SECOND", "MILLISECOND", "MICROSECOND"} +) + +# Substrait canonical unit name -> Polars-style duration-string suffix. +# Narwhals dt.truncate/dt.offset_by use the same combined "" +# format as Polars (verified against narwhals 2.24.0 on both dialects) -- +# bare Substrait names ("YEAR") are NOT accepted, unlike ibis. +_NARWHALS_UNIT_SUFFIX = { + "YEAR": "y", "MONTH": "mo", "DAY": "d", + "HOUR": "h", "MINUTE": "m", "SECOND": "s", + "MILLISECOND": "ms", "MICROSECOND": "us", +} + + +def _round_datetime(x: NarwhalsExpr, rounding: str, unit: str, multiple: int) -> NarwhalsExpr: + """Shared round_temporal/round_calendar implementation (unit != WEEK). + + Narwhals has no native round/ceil/tie primitive at all -- every mode + but FLOOR is hand-rolled from dt.truncate + dt.offset_by + duration + comparison via total_seconds() (verified against narwhals 2.24.0 on + both narwhals-polars and narwhals-pandas, including calendar units and + multiple > 1, e.g. "3mo" for a quarter). + """ + every = f"{multiple}{_NARWHALS_UNIT_SUFFIX[unit]}" + floor = x.dt.truncate(every) + if rounding == "FLOOR": + return floor + ceiling = nw.when(floor == x).then(x).otherwise(floor.dt.offset_by(every)) + if rounding == "CEIL": + return ceiling + diff_down = (x - floor).dt.total_seconds() + diff_up = (ceiling - x).dt.total_seconds() + if rounding == "ROUND_TIE_UP": + return nw.when(diff_up <= diff_down).then(ceiling).otherwise(floor) + # ROUND_TIE_DOWN: nearest, tying to the earlier point on equidistance. + return nw.when(diff_down <= diff_up).then(floor).otherwise(ceiling) + class SubstraitNarwhalsScalarDatetimeExpressionSystem(NarwhalsBaseExpressionSystem, SubstraitScalarDatetimeExpressionSystemProtocol[nw.Expr]): """Narwhals implementation of ScalarDatetimeExpressionProtocol. @@ -391,53 +433,78 @@ def round_temporal( self, x: NarwhalsExpr, /, - rounding: Optional[str] = None, - unit: str = "1d", + rounding: str, + unit: str, multiple: int = 1, - origin: Optional[str] = None, + origin: Any = None, ) -> NarwhalsExpr: - """Round datetime to a multiple of a time unit. + """Round datetime to a multiple of a fixed-duration time unit. Args: x: Datetime expression. rounding: FLOOR, CEIL, ROUND_TIE_DOWN, or ROUND_TIE_UP. - unit: Time unit string. - multiple: Multiple of unit (default 1). - origin: Origin timestamp for rounding. + unit: One of DAY, HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND. + YEAR/MONTH/WEEK are declared UNSUPPORTED here (ambiguous + fixed-duration length; see capabilities/datetime/rounding.py) + -- the raise below is defence in depth. + multiple: Positive multiplier on unit. Defaults to 1. + origin: Never non-None here -- the API builder rejects it at + build time (v1 scope). Returns: Rounded datetime. - - Note: - Narwhals only supports truncate. Falls back to truncate for all modes. """ - # Narwhals only has truncate - use for all rounding modes - return x.dt.truncate(unit) + if unit not in _ROUND_TEMPORAL_UNITS: + from mountainash.core.types import BackendCapabilityError + from mountainash.expressions.core.expression_system.function_keys.enums import ( + FKEY_SUBSTRAIT_SCALAR_DATETIME, + ) + + raise BackendCapabilityError( + f"round_temporal unit={unit!r} is not supported -- fixed-duration " + "rounding is ambiguous for YEAR/MONTH/WEEK; use round_calendar", + backend="narwhals", + function_key=FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_TEMPORAL, + ) + return _round_datetime(x, rounding, unit, multiple) def round_calendar( self, x: NarwhalsExpr, /, - rounding: Optional[str] = None, - unit: str = "1d", - origin: Optional[str] = None, + rounding: str, + unit: str, multiple: int = 1, + origin: Any = None, ) -> NarwhalsExpr: - """Round datetime to a calendar unit. - - Similar to round_temporal but for calendar-aware rounding. + """Round datetime to a multiple of a calendar time unit. Args: x: Datetime expression. rounding: FLOOR, CEIL, ROUND_TIE_DOWN, or ROUND_TIE_UP. - unit: Calendar unit string. - origin: Origin timestamp. - multiple: Multiple of unit. + unit: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, or + MICROSECOND. WEEK is declared UNSUPPORTED (narwhals' + ``dt.truncate`` rejects the "1w" duration on both dialects, + verified 2026-08-16 -- matches the existing MA + truncate/floor_dt "1w" gap in capabilities/datetime/options.py) + -- the raise below is defence in depth. + multiple: Positive multiplier on unit. Defaults to 1. + origin: Never non-None here -- the API builder rejects it at + build time (v1 scope). Returns: Rounded datetime. - - Note: - Narwhals only supports truncate. Falls back to truncate. """ - return self.round_temporal(x, rounding, unit, multiple, origin) + if unit == "WEEK": + from mountainash.core.types import BackendCapabilityError + from mountainash.expressions.core.expression_system.function_keys.enums import ( + FKEY_SUBSTRAIT_SCALAR_DATETIME, + ) + + raise BackendCapabilityError( + "round_calendar unit='WEEK' is not supported on narwhals -- " + "dt.truncate rejects the '1w' duration on both dialects", + backend="narwhals", + function_key=FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_CALENDAR, + ) + return _round_datetime(x, rounding, unit, multiple) diff --git a/tests/expressions/cross_backend/test_datetime_rounding.py b/tests/expressions/cross_backend/test_datetime_rounding.py index 960551fb..3416becc 100644 --- a/tests/expressions/cross_backend/test_datetime_rounding.py +++ b/tests/expressions/cross_backend/test_datetime_rounding.py @@ -165,10 +165,28 @@ def test_raises_capability_error(self, backend_name, rounding, backend_factory, collect_expr(df, expr) +WEEK_CAPABLE_BACKENDS = [ + b for b in TEMPORAL_BACKENDS if b not in ("narwhals-polars", "narwhals-pandas") +] + + @pytest.mark.cross_backend @pytest.mark.parametrize("backend_name", TEMPORAL_BACKENDS) -class TestRoundCalendarFloorCeilWeekDay: - """WEEK and DAY are supported on every backend, including ibis-polars.""" +class TestRoundCalendarFloorCeilDay: + """DAY is supported on every backend.""" + + def test_floor_day(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + actual = collect_expr( + df, ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="DAY") + ) + assert actual == [datetime(2026, 3, 15, 0, 0, 0)] + + +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", WEEK_CAPABLE_BACKENDS) +class TestRoundCalendarFloorCeilWeek: + """narwhals dt.truncate rejects the '1w' duration on both dialects.""" def test_floor_week(self, backend_name, backend_factory, collect_expr): # 2026-03-15 is a Sunday; ISO week starts Monday 2026-03-09. @@ -179,6 +197,15 @@ def test_floor_week(self, backend_name, backend_factory, collect_expr): assert actual == [datetime(2026, 3, 9, 0, 0, 0)] +@pytest.mark.cross_backend +@pytest.mark.parametrize("backend_name", ["narwhals-polars", "narwhals-pandas"]) +class TestRoundCalendarNarwhalsWeekUnsupported: + def test_raises_capability_error(self, backend_name, backend_factory, collect_expr): + df = backend_factory.create(_TIE_DATA, backend_name) + expr = ma.col("ts").dt.round_calendar(rounding="FLOOR", unit="WEEK") + with pytest.raises(BackendCapabilityError, match="round_calendar"): + collect_expr(df, expr) + @pytest.mark.cross_backend @pytest.mark.parametrize("backend_name", MONTH_YEAR_CAPABLE_BACKENDS) class TestRoundCalendarFloorCeilMonthYear: From 32b0344f4d45ef73f8e16c44ecef14f413c1e039 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Sun, 16 Aug 2026 23:47:55 +1000 Subject: [PATCH 7/8] feat(round_temporal): redirect MA truncate/round/ceil/floor wrappers, audit datetime capability facts (item 74 Task 7-8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Redirect polars/ibis/narwhals MA truncate/round_dt/ceil_dt/floor_dt wrappers through the real round_temporal/round_calendar implementation (item 74 Task 4-6) instead of the old x.truncate("")-based approximation that silently fell back to truncate for round/ceil and rejected multi-digit multipliers on ibis-duckdb/narwhals. - Retire now-false DURATION_MULTIPLIER capability facts in value_classes_ma.py: every fixture honors every multiplier value via the real redirect (re-probed 2026-08-16, disposition table in module docstring). Only ibis-sqlite's TimestampBucket compilation gap remains, documented as a residual note (no compilation rule exists on that dialect for any rounding op — tracked as a backlog follow-up, not a fact, since MATRIX_UNREACHABLE_DIALECT_FACTS already exempts it structurally). - Add rounding.py capability module declaring ibis-sqlite's structural gap for round_temporal/round_calendar (all rounding units unsupported on that dialect — no compilation rule). - Fix options.py/value_classes_ma.py declaration evidence fixtures to reflect each module's actual probed scope, resolving a duplicate CapabilityDeclaration identity collision across MOUNTAINASH-source IBIS declarations. - Wire rounding.py into the capability protocol guard's expected module list and domain-predicate table. - Add ROUND_TEMPORAL/ROUND_CALENDAR smoke-test arg overrides. - Retire stale test_multiplier_gate cross-backend regression test (asserted BackendCapabilityError on cells now honored); keep assume_timezone IANA_TIMEZONE coverage. - Remove now-XPASSing strict-xfail entries for ibis-duckdb/narwhals round/ceil in test_datetime_unit_dispatch.py's fallback-divergence map — both ops are real implementations now. - Drain KNOWN_UNTESTED_OPTION_PARAMS rounding/unit/multiple parks with a live-checked reason (dedicated cross-backend test file coverage, verified via _round_temporal_calendar_still_works()). - Regenerate docs/reference/expression-coverage{.md,.json,-scoped.md}. Full suite: tests/expressions/ + tests/core/ all green (17701 passed, 65 skipped, 3400 xfailed, 0 failed). ruff clean. --- docs/reference/expression-coverage-scoped.md | 76 +- docs/reference/expression-coverage.json | 12253 ++++++---------- docs/reference/expression-coverage.md | 33 +- .../backends/capabilities/datetime/options.py | 185 +- .../capabilities/datetime/value_classes_ma.py | 138 +- .../expsys_ib_ext_ma_scalar_datetime.py | 65 +- .../substrait/expsys_ib_scalar_datetime.py | 2 +- .../expsys_nw_ext_ma_scalar_datetime.py | 32 +- .../substrait/expsys_nw_scalar_datetime.py | 2 +- .../expsys_pl_ext_ma_scalar_datetime.py | 28 +- .../_ma_option_domains.py | 38 + tests/core/_smoke_helpers.py | 8 + tests/core/test_capability_protocol_guard.py | 3 +- .../argument_types/option_disposition.py | 6 + .../argument_types/test_arg_types_datetime.py | 101 +- .../argument_types/test_coverage_guard.py | 74 +- .../test_option_fact_integrity.py | 36 + .../test_datetime_unit_dispatch.py | 20 +- .../test_datetime_value_class_dispatch.py | 65 +- 19 files changed, 5073 insertions(+), 8092 deletions(-) diff --git a/docs/reference/expression-coverage-scoped.md b/docs/reference/expression-coverage-scoped.md index 3255d05b..81f088c6 100644 --- a/docs/reference/expression-coverage-scoped.md +++ b/docs/reference/expression-coverage-scoped.md @@ -5,7 +5,7 @@ Scoped deviations — dialect, parameter, option, value-class; function-level coverage and matrices live in [`expression-coverage.md`](expression-coverage.md). -Declarations: 42 · Facts: 1558 · Registered operations: 324 · Implementation records: 972 +Declarations: 43 · Facts: 1460 · Registered operations: 326 · Implementation records: 978 Legend — scoped deviations: @@ -131,57 +131,48 @@ Legend — scoped deviations: | Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| narwhals-pandas | unit | — | duration_multiplier | unsupported | gate | build | — | narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result | — | — | 2026-07-25 | — | — | -| narwhals-pandas | unit | 1d, 1h, 1m, 1mo, 1ms, 1q, 1s, 1us, 1w, 1y, day, hour, microsecond, millisecond, minute, month, quarter, second, week, year | — | unsupported | gate | build | — | narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value | — | — | 2026-07-24 | — | — | -| narwhals-polars | unit | — | duration_multiplier | unsupported | gate | build | — | narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result | — | — | 2026-07-25 | — | — | -| narwhals-polars | unit | 1d, 1h, 1m, 1mo, 1ms, 1q, 1s, 1us, 1w, 1y, day, hour, microsecond, millisecond, minute, month, quarter, second, week, year | — | unsupported | gate | build | — | narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value | — | — | 2026-07-24 | — | — | +| narwhals-pandas | unit | 1w | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-pandas | unit | week | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-polars | unit | 1w | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-polars | unit | week | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | ### `CEIL` × ibis (FKEY_MOUNTAINASH_SCALAR_DATETIME) | Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| * | unit | — | duration_multiplier | unsupported | gate | build | — | ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted | — | — | 2026-07-25 | — | — | -| * | unit | 1d, 1h, 1m, 1mo, 1ms, 1q, 1s, 1us, 1w, 1y, day, hour, microsecond, millisecond, minute, month, quarter, second, week, year | — | unsupported | gate | build | — | ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value | — | — | 2026-07-24 | — | — | -| ibis-duckdb | unit | — | duration_multiplier | unsupported | gate | build | — | ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted | — | — | 2026-07-25 | — | — | -| ibis-duckdb | unit | 1d, 1h, 1m, 1mo, 1ms, 1q, 1s, 1us, 1w, 1y, day, hour, microsecond, millisecond, minute, month, quarter, second, week, year | — | unsupported | gate | build | — | ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value | — | — | 2026-07-24 | — | — | +| ibis-polars | unit | 1mo, 1q, 1y, month, quarter, year | — | unsupported | gate | build | — | ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | +| ibis-sqlite | unit | 1h, 1m, 1ms, 1q, 1s, 1us, hour, microsecond, millisecond, minute, quarter, second | — | unsupported | gate | build | — | ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | ### `FLOOR` × narwhals (FKEY_MOUNTAINASH_SCALAR_DATETIME) | Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| narwhals-pandas | unit | 1w | — | unsupported | gate | build | — | narwhals truncate rejects the week unit '1w' (and its friendly alias 'week') | — | — | 2026-07-24 | — | — | -| narwhals-pandas | unit | week | — | unsupported | gate | build | — | narwhals truncate rejects the week unit '1w' (and its friendly alias 'week') | — | — | 2026-07-24 | — | — | -| narwhals-polars | unit | 1w | — | unsupported | gate | build | — | narwhals truncate rejects the week unit '1w' (and its friendly alias 'week') | — | — | 2026-07-24 | — | — | -| narwhals-polars | unit | week | — | unsupported | gate | build | — | narwhals truncate rejects the week unit '1w' (and its friendly alias 'week') | — | — | 2026-07-24 | — | — | +| narwhals-pandas | unit | 1w | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-pandas | unit | week | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-polars | unit | 1w | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-polars | unit | week | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | ### `FLOOR` × ibis (FKEY_MOUNTAINASH_SCALAR_DATETIME) | Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| * | unit | — | duration_multiplier | unsupported | gate | build | — | ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted | — | — | 2026-07-25 | — | — | -| * | unit | 1q | — | unsupported | gate | build | — | ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter') | — | — | 2026-07-24 | — | — | -| * | unit | quarter | — | unsupported | gate | build | — | ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter') | — | — | 2026-07-24 | — | — | -| ibis-duckdb | unit | — | duration_multiplier | unsupported | gate | build | — | ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted | — | — | 2026-07-25 | — | — | -| ibis-duckdb | unit | 1q | — | unsupported | gate | build | — | ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter') | — | — | 2026-07-24 | — | — | -| ibis-duckdb | unit | quarter | — | unsupported | gate | build | — | ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter') | — | — | 2026-07-24 | — | — | +| ibis-sqlite | unit | 1h, 1m, 1ms, 1q, 1s, 1us, hour, microsecond, millisecond, minute, quarter, second | — | unsupported | gate | build | — | ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | ### `ROUND` × narwhals (FKEY_MOUNTAINASH_SCALAR_DATETIME) | Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| narwhals-pandas | unit | — | duration_multiplier | unsupported | gate | build | — | narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result | — | — | 2026-07-25 | — | — | -| narwhals-pandas | unit | 1d, 1h, 1m, 1mo, 1ms, 1q, 1s, 1us, 1w, 1y, day, hour, microsecond, millisecond, minute, month, quarter, second, week, year | — | unsupported | gate | build | — | narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value | — | — | 2026-07-24 | — | — | -| narwhals-polars | unit | — | duration_multiplier | unsupported | gate | build | — | narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result | — | — | 2026-07-25 | — | — | -| narwhals-polars | unit | 1d, 1h, 1m, 1mo, 1ms, 1q, 1s, 1us, 1w, 1y, day, hour, microsecond, millisecond, minute, month, quarter, second, week, year | — | unsupported | gate | build | — | narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value | — | — | 2026-07-24 | — | — | +| narwhals-pandas | unit | 1w | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-pandas | unit | week | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-polars | unit | 1w | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-polars | unit | week | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | ### `ROUND` × ibis (FKEY_MOUNTAINASH_SCALAR_DATETIME) | Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| * | unit | — | duration_multiplier | unsupported | gate | build | — | ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted | — | — | 2026-07-25 | — | — | -| * | unit | 1d, 1h, 1m, 1mo, 1ms, 1q, 1s, 1us, 1w, 1y, day, hour, microsecond, millisecond, minute, month, quarter, second, week, year | — | unsupported | gate | build | — | ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value | — | — | 2026-07-24 | — | — | -| ibis-duckdb | unit | — | duration_multiplier | unsupported | gate | build | — | ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted | — | — | 2026-07-25 | — | — | -| ibis-duckdb | unit | 1d, 1h, 1m, 1mo, 1ms, 1q, 1s, 1us, 1w, 1y, day, hour, microsecond, millisecond, minute, month, quarter, second, week, year | — | unsupported | gate | build | — | ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value | — | — | 2026-07-24 | — | — | +| ibis-polars | unit | 1mo, 1q, 1y, month, quarter, year | — | unsupported | gate | build | — | ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | +| ibis-sqlite | unit | 1h, 1m, 1ms, 1q, 1s, 1us, hour, microsecond, millisecond, minute, quarter, second | — | unsupported | gate | build | — | ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | ### `TO_TIMEZONE` × ibis (FKEY_MOUNTAINASH_SCALAR_DATETIME) @@ -194,21 +185,16 @@ Legend — scoped deviations: | Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| narwhals-pandas | unit | 1w | — | unsupported | gate | build | — | narwhals truncate rejects the week unit '1w' (and its friendly alias 'week') | — | — | 2026-07-24 | — | — | -| narwhals-pandas | unit | week | — | unsupported | gate | build | — | narwhals truncate rejects the week unit '1w' (and its friendly alias 'week') | — | — | 2026-07-24 | — | — | -| narwhals-polars | unit | 1w | — | unsupported | gate | build | — | narwhals truncate rejects the week unit '1w' (and its friendly alias 'week') | — | — | 2026-07-24 | — | — | -| narwhals-polars | unit | week | — | unsupported | gate | build | — | narwhals truncate rejects the week unit '1w' (and its friendly alias 'week') | — | — | 2026-07-24 | — | — | +| narwhals-pandas | unit | 1w | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-pandas | unit | week | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-polars | unit | 1w | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | +| narwhals-polars | unit | week | — | unsupported | gate | build | — | narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects | — | — | 2026-08-16 | — | — | ### `TRUNCATE` × ibis (FKEY_MOUNTAINASH_SCALAR_DATETIME) | Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| * | unit | — | duration_multiplier | unsupported | gate | build | — | ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted | — | — | 2026-07-25 | — | — | -| * | unit | 1q | — | unsupported | gate | build | — | ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter') | — | — | 2026-07-24 | — | — | -| * | unit | quarter | — | unsupported | gate | build | — | ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter') | — | — | 2026-07-24 | — | — | -| ibis-duckdb | unit | — | duration_multiplier | unsupported | gate | build | — | ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted | — | — | 2026-07-25 | — | — | -| ibis-duckdb | unit | 1q | — | unsupported | gate | build | — | ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter') | — | — | 2026-07-24 | — | — | -| ibis-duckdb | unit | quarter | — | unsupported | gate | build | — | ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter') | — | — | 2026-07-24 | — | — | +| ibis-sqlite | unit | 1h, 1m, 1ms, 1q, 1s, 1us, hour, microsecond, millisecond, minute, quarter, second | — | unsupported | gate | build | — | ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | ### `CONTAINS` × narwhals (FKEY_MOUNTAINASH_SCALAR_LIST) @@ -1014,6 +1000,22 @@ Legend — scoped deviations: | * | timezone | — | iana_timezone | unsupported | gate | build | — | local_timestamp returns the UTC wall clock, not the target-zone wall clock -- ibis has no timezone method and the naive re-cast discards the conversion (verified 2026-07-29, ibis 12.0.0/duckdb: 12:00 instead of 17:30 for Asia/Kolkata) | — | — | 2026-07-29 | — | — | | ibis-duckdb | timezone | — | iana_timezone | unsupported | gate | build | — | local_timestamp returns the UTC wall clock, not the target-zone wall clock -- ibis has no timezone method and the naive re-cast discards the conversion (verified 2026-07-29, ibis 12.0.0/duckdb: 12:00 instead of 17:30 for Asia/Kolkata) | — | — | 2026-07-29 | — | — | +### `ROUND_CALENDAR` × ibis (FKEY_SUBSTRAIT_SCALAR_DATETIME) + +| Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| ibis-polars | unit | MONTH | — | unsupported | gate | build | — | ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- CEIL/ROUND_TIE_DOWN/ROUND_TIE_UP cannot compute the next calendar boundary; verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | +| ibis-polars | unit | YEAR | — | unsupported | gate | build | — | ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- CEIL/ROUND_TIE_DOWN/ROUND_TIE_UP cannot compute the next calendar boundary; verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | +| ibis-sqlite | multiple | 3 | — | unsupported | gate | build | — | ibis-sqlite has no TimestampBucket compilation rule -- multiple > 1 is unsupported for every unit; verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | +| ibis-sqlite | unit | HOUR, MICROSECOND, MILLISECOND, MINUTE, SECOND | — | unsupported | gate | build | — | ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | + +### `ROUND_TEMPORAL` × ibis (FKEY_SUBSTRAIT_SCALAR_DATETIME) + +| Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| ibis-sqlite | multiple | 2 | — | unsupported | gate | build | — | ibis-sqlite has no TimestampBucket compilation rule -- multiple > 1 is unsupported for every unit; verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | +| ibis-sqlite | unit | HOUR, MICROSECOND, MILLISECOND, MINUTE, SECOND | — | unsupported | gate | build | — | ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | + ### `STRPTIME_DATE` × narwhals (FKEY_SUBSTRAIT_SCALAR_DATETIME) #### Dialect-scoped whole-op diff --git a/docs/reference/expression-coverage.json b/docs/reference/expression-coverage.json index 0f7d12e9..9d4a97c7 100644 --- a/docs/reference/expression-coverage.json +++ b/docs/reference/expression-coverage.json @@ -1,68 +1,68 @@ { "stamp": { - "declarations": 42, - "facts": 1558, - "operations": 324, - "implementation_records": 972 + "declarations": 43, + "facts": 1460, + "operations": 326, + "implementation_records": 978 }, "stats": { "backends": { "polars": { "by_impl": { - "implemented": 321, + "implemented": 323, "implemented_via_handler": 3, "not_implemented": 0, "unknown": 0 }, "default_capable": 191, - "audited_clean": 81, + "audited_clean": 83, "constrained": 52, "audited_unknown": 0 }, "narwhals": { "by_impl": { - "implemented": 321, + "implemented": 323, "implemented_via_handler": 3, "not_implemented": 0, "unknown": 0 }, "default_capable": 97, - "audited_clean": 149, + "audited_clean": 151, "constrained": 78, "audited_unknown": 0 }, "ibis": { "by_impl": { - "implemented": 321, + "implemented": 323, "implemented_via_handler": 3, "not_implemented": 0, "unknown": 0 }, "default_capable": 138, "audited_clean": 112, - "constrained": 74, + "constrained": 76, "audited_unknown": 0 } }, "contradictions": 0, - "ops_total": 324, + "ops_total": 326, "facts_by_level": { "expr_capable": 149, "literal_only": 65, "polymorphic": 9, - "unsupported": 1335 + "unsupported": 1237 }, "facts_by_enforcement": { - "gate": 1551, + "gate": 1453, "router_metadata": 3, "materialize_residue": 4 }, "facts_by_backend": { "polars": 265, - "narwhals": 662, - "ibis": 631 + "narwhals": 586, + "ibis": 609 }, - "facts_total": 1558 + "facts_total": 1460 }, "families": [ { @@ -1719,109 +1719,113 @@ "contradiction": false, "selector_counts": { "params": 1, - "option_selectors": 20, - "value_classes": 1, + "option_selectors": 2, + "value_classes": 0, "dialects": 2 }, "constraints": [ { "dialect": "narwhals-pandas", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1d", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { "dialect": "narwhals-pandas", "param": "unit", - "option_value": "1h", + "option_value": "week", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "narwhals-polars", "param": "unit", - "option_value": "1m", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "narwhals-polars", "param": "unit", - "option_value": "1mo", + "option_value": "week", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null - }, + } + ], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "ceil_dt", + "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": true, + "contradiction": false, + "selector_counts": { + "params": 1, + "option_selectors": 16, + "value_classes": 0, + "dialects": 2 + }, + "constraints": [ { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", - "option_value": "1ms", + "option_value": "1mo", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "1q", "value_class": null, @@ -1829,63 +1833,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "1y", "value_class": null, @@ -1893,95 +1849,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "month", "value_class": null, @@ -1989,15 +1865,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "quarter", "value_class": null, @@ -2005,47 +1881,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "year", "value_class": null, @@ -2053,47 +1897,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1h", "value_class": null, @@ -2101,15 +1913,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1m", "value_class": null, @@ -2117,31 +1929,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1ms", "value_class": null, @@ -2149,15 +1945,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1q", "value_class": null, @@ -2165,15 +1961,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1s", "value_class": null, @@ -2181,15 +1977,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1us", "value_class": null, @@ -2197,63 +1993,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "hour", "value_class": null, @@ -2261,15 +2009,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "microsecond", "value_class": null, @@ -2277,15 +2025,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "millisecond", "value_class": null, @@ -2293,15 +2041,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "minute", "value_class": null, @@ -2309,31 +2057,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "quarter", "value_class": null, @@ -2341,15 +2073,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "second", "value_class": null, @@ -2357,42 +2089,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null } @@ -2400,695 +2100,68 @@ "residue": [], "routed": [], "refinements": [] + } + } + }, + { + "op": { + "family": "FKEY_MOUNTAINASH_SCALAR_DATETIME", + "op": "DATE" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "date", + "impl_protocol": "MountainAshPolarsScalarDatetimeExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "date", + "impl_protocol": "MountainAshNarwhalsScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] }, "ibis": { "impl": "implemented", - "impl_method": "ceil_dt", + "impl_method": "date", "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, - "constrained": true, + "constrained": false, "contradiction": false, "selector_counts": { - "params": 1, - "option_selectors": 20, - "value_classes": 1, - "dialects": 1 + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 }, - "constraints": [ - { - "dialect": null, - "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1h", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1m", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1ms", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1h", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1m", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1ms", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - } - ], + "constraints": [], "residue": [], "routed": [], "refinements": [] @@ -3098,12 +2171,12 @@ { "op": { "family": "FKEY_MOUNTAINASH_SCALAR_DATETIME", - "op": "DATE" + "op": "DAYS_IN_MONTH" }, "cells": { "polars": { "impl": "implemented", - "impl_method": "date", + "impl_method": "days_in_month", "impl_protocol": "MountainAshPolarsScalarDatetimeExpressionSystem", "audited": false, "whole_op": null, @@ -3122,7 +2195,7 @@ }, "narwhals": { "impl": "implemented", - "impl_method": "date", + "impl_method": "days_in_month", "impl_protocol": "MountainAshNarwhalsScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, @@ -3141,7 +2214,7 @@ }, "ibis": { "impl": "implemented", - "impl_method": "date", + "impl_method": "days_in_month", "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, @@ -3163,12 +2236,12 @@ { "op": { "family": "FKEY_MOUNTAINASH_SCALAR_DATETIME", - "op": "DAYS_IN_MONTH" + "op": "DIFF_DAYS" }, "cells": { "polars": { "impl": "implemented", - "impl_method": "days_in_month", + "impl_method": "diff_days", "impl_protocol": "MountainAshPolarsScalarDatetimeExpressionSystem", "audited": false, "whole_op": null, @@ -3187,7 +2260,7 @@ }, "narwhals": { "impl": "implemented", - "impl_method": "days_in_month", + "impl_method": "diff_days", "impl_protocol": "MountainAshNarwhalsScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, @@ -3206,7 +2279,7 @@ }, "ibis": { "impl": "implemented", - "impl_method": "days_in_month", + "impl_method": "diff_days", "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, @@ -3228,12 +2301,12 @@ { "op": { "family": "FKEY_MOUNTAINASH_SCALAR_DATETIME", - "op": "DIFF_DAYS" + "op": "DIFF_HOURS" }, "cells": { "polars": { "impl": "implemented", - "impl_method": "diff_days", + "impl_method": "diff_hours", "impl_protocol": "MountainAshPolarsScalarDatetimeExpressionSystem", "audited": false, "whole_op": null, @@ -3252,7 +2325,7 @@ }, "narwhals": { "impl": "implemented", - "impl_method": "diff_days", + "impl_method": "diff_hours", "impl_protocol": "MountainAshNarwhalsScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, @@ -3271,7 +2344,7 @@ }, "ibis": { "impl": "implemented", - "impl_method": "diff_days", + "impl_method": "diff_hours", "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, @@ -3293,12 +2366,12 @@ { "op": { "family": "FKEY_MOUNTAINASH_SCALAR_DATETIME", - "op": "DIFF_HOURS" + "op": "DIFF_MILLISECONDS" }, "cells": { "polars": { "impl": "implemented", - "impl_method": "diff_hours", + "impl_method": "diff_milliseconds", "impl_protocol": "MountainAshPolarsScalarDatetimeExpressionSystem", "audited": false, "whole_op": null, @@ -3317,72 +2390,7 @@ }, "narwhals": { "impl": "implemented", - "impl_method": "diff_hours", - "impl_protocol": "MountainAshNarwhalsScalarDatetimeExpressionSystem", - "audited": true, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "ibis": { - "impl": "implemented", - "impl_method": "diff_hours", - "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", - "audited": true, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - } - } - }, - { - "op": { - "family": "FKEY_MOUNTAINASH_SCALAR_DATETIME", - "op": "DIFF_MILLISECONDS" - }, - "cells": { - "polars": { - "impl": "implemented", - "impl_method": "diff_milliseconds", - "impl_protocol": "MountainAshPolarsScalarDatetimeExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "narwhals": { - "impl": "implemented", - "impl_method": "diff_milliseconds", + "impl_method": "diff_milliseconds", "impl_protocol": "MountainAshNarwhalsScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, @@ -4769,10 +3777,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, @@ -4785,10 +3793,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, @@ -4801,10 +3809,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, @@ -4817,10 +3825,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null } @@ -4839,29 +3847,61 @@ "contradiction": false, "selector_counts": { "params": 1, - "option_selectors": 2, - "value_classes": 1, + "option_selectors": 12, + "value_classes": 0, "dialects": 1 }, "constraints": [ { - "dialect": null, + "dialect": "ibis-sqlite", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", + "option_value": "1h", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1m", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1ms", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1q", "value_class": null, @@ -4869,63 +3909,111 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-sqlite", "param": "unit", - "option_value": "quarter", + "option_value": "1s", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": "ibis-sqlite", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", + "option_value": "1us", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": "ibis-sqlite", "param": "unit", - "option_value": "1q", + "option_value": "hour", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "microsecond", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "millisecond", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "minute", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", "param": "unit", "option_value": "quarter", "value_class": null, @@ -4933,10 +4021,26 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "second", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null } @@ -5372,109 +4476,113 @@ "contradiction": false, "selector_counts": { "params": 1, - "option_selectors": 20, - "value_classes": 1, + "option_selectors": 2, + "value_classes": 0, "dialects": 2 }, "constraints": [ { "dialect": "narwhals-pandas", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1d", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { "dialect": "narwhals-pandas", "param": "unit", - "option_value": "1h", + "option_value": "week", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "narwhals-polars", "param": "unit", - "option_value": "1m", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "narwhals-polars", "param": "unit", - "option_value": "1mo", + "option_value": "week", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null - }, + } + ], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "round_dt", + "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": true, + "contradiction": false, + "selector_counts": { + "params": 1, + "option_selectors": 16, + "value_classes": 0, + "dialects": 2 + }, + "constraints": [ { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", - "option_value": "1ms", + "option_value": "1mo", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "1q", "value_class": null, @@ -5482,63 +4590,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "1y", "value_class": null, @@ -5546,95 +4606,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "month", "value_class": null, @@ -5642,15 +4622,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "quarter", "value_class": null, @@ -5658,47 +4638,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "ibis-polars", "param": "unit", "option_value": "year", "value_class": null, @@ -5706,47 +4654,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1h", "value_class": null, @@ -5754,15 +4670,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1m", "value_class": null, @@ -5770,31 +4686,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1ms", "value_class": null, @@ -5802,15 +4702,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1q", "value_class": null, @@ -5818,15 +4718,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1s", "value_class": null, @@ -5834,15 +4734,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1us", "value_class": null, @@ -5850,63 +4750,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "hour", "value_class": null, @@ -5914,15 +4766,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "microsecond", "value_class": null, @@ -5930,15 +4782,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "millisecond", "value_class": null, @@ -5946,15 +4798,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "minute", "value_class": null, @@ -5962,31 +4814,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "quarter", "value_class": null, @@ -5994,15 +4830,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "ibis-sqlite", "param": "unit", "option_value": "second", "value_class": null, @@ -6010,42 +4846,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null } @@ -6053,758 +4857,66 @@ "residue": [], "routed": [], "refinements": [] + } + } + }, + { + "op": { + "family": "FKEY_MOUNTAINASH_SCALAR_DATETIME", + "op": "TIME" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "time", + "impl_protocol": "MountainAshPolarsScalarDatetimeExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "time", + "impl_protocol": "MountainAshNarwhalsScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] }, "ibis": { "impl": "implemented", - "impl_method": "round_dt", + "impl_method": "time", "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, - "constrained": true, + "constrained": false, "contradiction": false, "selector_counts": { - "params": 1, - "option_selectors": 20, - "value_classes": 1, - "dialects": 1 - }, - "constraints": [ - { - "dialect": null, - "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1h", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1m", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1ms", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1h", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1m", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1ms", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - } - ], - "residue": [], - "routed": [], - "refinements": [] - } - } - }, - { - "op": { - "family": "FKEY_MOUNTAINASH_SCALAR_DATETIME", - "op": "TIME" - }, - "cells": { - "polars": { - "impl": "implemented", - "impl_method": "time", - "impl_protocol": "MountainAshPolarsScalarDatetimeExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "narwhals": { - "impl": "implemented", - "impl_method": "time", - "impl_protocol": "MountainAshNarwhalsScalarDatetimeExpressionSystem", - "audited": true, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "ibis": { - "impl": "implemented", - "impl_method": "time", - "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", - "audited": true, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 }, "constraints": [], "residue": [], @@ -7480,10 +5592,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, @@ -7496,10 +5608,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, @@ -7512,10 +5624,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, @@ -7528,10 +5640,10 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null } @@ -7550,29 +5662,61 @@ "contradiction": false, "selector_counts": { "params": 1, - "option_selectors": 2, - "value_classes": 1, + "option_selectors": 12, + "value_classes": 0, "dialects": 1 }, "constraints": [ { - "dialect": null, + "dialect": "ibis-sqlite", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", + "option_value": "1h", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1m", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1ms", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", "param": "unit", "option_value": "1q", "value_class": null, @@ -7580,63 +5724,111 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-sqlite", "param": "unit", - "option_value": "quarter", + "option_value": "1s", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": "ibis-sqlite", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", + "option_value": "1us", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": "ibis-sqlite", "param": "unit", - "option_value": "1q", + "option_value": "hour", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "microsecond", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "millisecond", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "minute", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", "param": "unit", "option_value": "quarter", "value_class": null, @@ -7644,10 +5836,26 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "second", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null } @@ -32167,6 +30375,362 @@ } } }, + { + "op": { + "family": "FKEY_SUBSTRAIT_SCALAR_DATETIME", + "op": "ROUND_CALENDAR" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "round_calendar", + "impl_protocol": "SubstraitPolarsScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "round_calendar", + "impl_protocol": "SubstraitNarwhalsScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "round_calendar", + "impl_protocol": "SubstraitIbisScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": true, + "contradiction": false, + "selector_counts": { + "params": 2, + "option_selectors": 8, + "value_classes": 0, + "dialects": 2 + }, + "constraints": [ + { + "dialect": "ibis-polars", + "param": "unit", + "option_value": "MONTH", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- CEIL/ROUND_TIE_DOWN/ROUND_TIE_UP cannot compute the next calendar boundary; verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-polars", + "param": "unit", + "option_value": "YEAR", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- CEIL/ROUND_TIE_DOWN/ROUND_TIE_UP cannot compute the next calendar boundary; verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "multiple", + "option_value": "3", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampBucket compilation rule -- multiple > 1 is unsupported for every unit; verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "HOUR", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MICROSECOND", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MILLISECOND", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MINUTE", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "SECOND", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + } + ], + "residue": [], + "routed": [], + "refinements": [] + } + } + }, + { + "op": { + "family": "FKEY_SUBSTRAIT_SCALAR_DATETIME", + "op": "ROUND_TEMPORAL" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "round_temporal", + "impl_protocol": "SubstraitPolarsScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "round_temporal", + "impl_protocol": "SubstraitNarwhalsScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "round_temporal", + "impl_protocol": "SubstraitIbisScalarDatetimeExpressionSystem", + "audited": true, + "whole_op": null, + "constrained": true, + "contradiction": false, + "selector_counts": { + "params": 2, + "option_selectors": 6, + "value_classes": 0, + "dialects": 1 + }, + "constraints": [ + { + "dialect": "ibis-sqlite", + "param": "multiple", + "option_value": "2", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite has no TimestampBucket compilation rule -- multiple > 1 is unsupported for every unit; verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "HOUR", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MICROSECOND", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MILLISECOND", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MINUTE", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "SECOND", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + } + ], + "residue": [], + "routed": [], + "refinements": [] + } + } + }, { "op": { "family": "FKEY_SUBSTRAIT_SCALAR_DATETIME", @@ -46143,2144 +44707,588 @@ "value_classes": 0, "dialects": 0 }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - } - } - }, - { - "op": { - "family": "SUBSTRAIT_ARITHMETIC_WINDOW", - "op": "LEAD" - }, - "cells": { - "polars": { - "impl": "implemented", - "impl_method": "lead", - "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "narwhals": { - "impl": "implemented", - "impl_method": "lead", - "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "ibis": { - "impl": "implemented", - "impl_method": "lead", - "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - } - } - }, - { - "op": { - "family": "SUBSTRAIT_ARITHMETIC_WINDOW", - "op": "NTH_VALUE" - }, - "cells": { - "polars": { - "impl": "implemented", - "impl_method": "nth_value", - "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "narwhals": { - "impl": "implemented", - "impl_method": "nth_value", - "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "ibis": { - "impl": "implemented", - "impl_method": "nth_value", - "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - } - } - }, - { - "op": { - "family": "SUBSTRAIT_ARITHMETIC_WINDOW", - "op": "NTILE" - }, - "cells": { - "polars": { - "impl": "implemented", - "impl_method": "ntile", - "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "narwhals": { - "impl": "implemented", - "impl_method": "ntile", - "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "ibis": { - "impl": "implemented", - "impl_method": "ntile", - "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - } - } - }, - { - "op": { - "family": "SUBSTRAIT_ARITHMETIC_WINDOW", - "op": "PERCENT_RANK" - }, - "cells": { - "polars": { - "impl": "implemented", - "impl_method": "percent_rank", - "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "narwhals": { - "impl": "implemented", - "impl_method": "percent_rank", - "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "ibis": { - "impl": "implemented", - "impl_method": "percent_rank", - "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - } - } - }, - { - "op": { - "family": "SUBSTRAIT_ARITHMETIC_WINDOW", - "op": "RANK" - }, - "cells": { - "polars": { - "impl": "implemented", - "impl_method": "rank", - "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "narwhals": { - "impl": "implemented", - "impl_method": "rank", - "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "ibis": { - "impl": "implemented", - "impl_method": "rank", - "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - } - } - }, - { - "op": { - "family": "SUBSTRAIT_ARITHMETIC_WINDOW", - "op": "ROW_NUMBER" - }, - "cells": { - "polars": { - "impl": "implemented", - "impl_method": "row_number", - "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "narwhals": { - "impl": "implemented", - "impl_method": "row_number", - "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - }, - "ibis": { - "impl": "implemented", - "impl_method": "row_number", - "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", - "audited": false, - "whole_op": null, - "constrained": false, - "contradiction": false, - "selector_counts": { - "params": 0, - "option_selectors": 0, - "value_classes": 0, - "dialects": 0 - }, - "constraints": [], - "residue": [], - "routed": [], - "refinements": [] - } - } - } - ] - } - ], - "declarations": [ - { - "backend": "ibis", - "source": "mountainash", - "domain": "datetime", - "evidence": { - "probe_date": "2026-07-05", - "library_versions": [], - "fixtures": [] - }, - "facts": [ - { - "dialect": null, - "param": "days", - "option_value": null, - "value_class": null, - "level": "literal_only", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "Ibis datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "IB-DT-01", - "since": "2026-07-05", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "hours", - "option_value": null, - "value_class": null, - "level": "literal_only", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "Ibis datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "IB-DT-01", - "since": "2026-07-05", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "microseconds", - "option_value": null, - "value_class": null, - "level": "literal_only", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "Ibis datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "IB-DT-01", - "since": "2026-07-05", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "milliseconds", - "option_value": null, - "value_class": null, - "level": "literal_only", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "Ibis datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "IB-DT-01", - "since": "2026-07-05", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "minutes", - "option_value": null, - "value_class": null, - "level": "literal_only", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "Ibis datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "IB-DT-01", - "since": "2026-07-05", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "months", - "option_value": null, - "value_class": null, - "level": "literal_only", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "Ibis datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "IB-DT-01", - "since": "2026-07-05", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "seconds", - "option_value": null, - "value_class": null, - "level": "literal_only", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "Ibis datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "IB-DT-01", - "since": "2026-07-05", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "years", - "option_value": null, - "value_class": null, - "level": "literal_only", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "Ibis datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "IB-DT-01", - "since": "2026-07-05", - "native_errors": [], - "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "mountainash", - "domain": "datetime", - "evidence": { - "probe_date": "2026-07-24", - "library_versions": [], - "fixtures": [ - "polars", - "ibis-duckdb", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ - { - "dialect": null, - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1h", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1h", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1m", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1m", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1ms", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1ms", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1d", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1h", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1h", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1m", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1m", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1mo", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1ms", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1ms", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1q", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1s", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1us", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1w", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "1y", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "day", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "hour", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "microsecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "millisecond", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "minute", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "month", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "quarter", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + } + } }, { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null + "op": { + "family": "SUBSTRAIT_ARITHMETIC_WINDOW", + "op": "LEAD" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "lead", + "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "lead", + "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "lead", + "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + } + } }, { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "second", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null + "op": { + "family": "SUBSTRAIT_ARITHMETIC_WINDOW", + "op": "NTH_VALUE" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "nth_value", + "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "nth_value", + "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "nth_value", + "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + } + } }, { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null + "op": { + "family": "SUBSTRAIT_ARITHMETIC_WINDOW", + "op": "NTILE" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "ntile", + "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "ntile", + "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "ntile", + "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + } + } }, { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "week", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null + "op": { + "family": "SUBSTRAIT_ARITHMETIC_WINDOW", + "op": "PERCENT_RANK" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "percent_rank", + "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "percent_rank", + "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "percent_rank", + "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + } + } }, { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null + "op": { + "family": "SUBSTRAIT_ARITHMETIC_WINDOW", + "op": "RANK" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "rank", + "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "rank", + "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "rank", + "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + } + } }, { - "dialect": "ibis-duckdb", - "param": "unit", - "option_value": "year", - "value_class": null, - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", - "native_errors": [], - "probe_exempt": null + "op": { + "family": "SUBSTRAIT_ARITHMETIC_WINDOW", + "op": "ROW_NUMBER" + }, + "cells": { + "polars": { + "impl": "implemented", + "impl_method": "row_number", + "impl_protocol": "SubstraitPolarsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "narwhals": { + "impl": "implemented", + "impl_method": "row_number", + "impl_protocol": "SubstraitNarwhalsWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + }, + "ibis": { + "impl": "implemented", + "impl_method": "row_number", + "impl_protocol": "SubstraitIbisWindowArithmeticExpressionSystem", + "audited": false, + "whole_op": null, + "constrained": false, + "contradiction": false, + "selector_counts": { + "params": 0, + "option_selectors": 0, + "value_classes": 0, + "dialects": 0 + }, + "constraints": [], + "residue": [], + "routed": [], + "refinements": [] + } + } } ] - }, + } + ], + "declarations": [ { "backend": "ibis", "source": "mountainash", "domain": "datetime", "evidence": { - "probe_date": "2026-07-25", + "probe_date": "2026-07-05", "library_versions": [], - "fixtures": [ - "polars", - "ibis-duckdb", - "narwhals-polars", - "narwhals-pandas" - ] + "fixtures": [] }, "facts": [ { "dialect": null, - "param": "timezone", + "param": "days", "option_value": null, - "value_class": "iana_timezone", - "level": "unsupported", + "value_class": null, + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "to_timezone is correct only at the materialization boundary -- the target zone lives in the ibis output dtype, not in the engine (SQL is a bare CAST AS TIMESTAMPTZ), so any expression composed on the result raises UnsupportedOperationError (verified 2026-07-29, ibis 12.0.0/duckdb)", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-29", + "message": "Ibis datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "IB-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "unit", + "param": "hours", "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", + "value_class": null, + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", + "message": "Ibis datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "IB-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "unit", + "param": "microseconds", "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", + "value_class": null, + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", + "message": "Ibis datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "IB-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "unit", + "param": "milliseconds", "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", + "value_class": null, + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", + "message": "Ibis datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "IB-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "unit", + "param": "minutes", "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", + "value_class": null, + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", + "message": "Ibis datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "IB-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "timezone", + "dialect": null, + "param": "months", "option_value": null, - "value_class": "iana_timezone", - "level": "unsupported", + "value_class": null, + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "to_timezone is correct only at the materialization boundary -- the target zone lives in the ibis output dtype, not in the engine (SQL is a bare CAST AS TIMESTAMPTZ), so any expression composed on the result raises UnsupportedOperationError (verified 2026-07-29, ibis 12.0.0/duckdb)", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-29", + "message": "Ibis datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "IB-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "unit", + "dialect": null, + "param": "seconds", "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", + "value_class": null, + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", + "message": "Ibis datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "IB-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "unit", + "dialect": null, + "param": "years", "option_value": null, - "value_class": "duration_multiplier", - "level": "unsupported", + "value_class": null, + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", + "message": "Ibis datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "IB-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "ibis", + "source": "mountainash", + "domain": "datetime", + "evidence": { + "probe_date": "2026-08-16", + "library_versions": [], + "fixtures": [ + "ibis-duckdb" + ] + }, + "facts": [ { - "dialect": "ibis-duckdb", - "param": "unit", + "dialect": null, + "param": "timezone", "option_value": null, - "value_class": "duration_multiplier", + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", + "message": "to_timezone is correct only at the materialization boundary -- the target zone lives in the ibis output dtype, not in the engine (SQL is a bare CAST AS TIMESTAMPTZ), so any expression composed on the result raises UnsupportedOperationError (verified 2026-07-29, ibis 12.0.0/duckdb)", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-07-29", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "unit", + "param": "timezone", "option_value": null, - "value_class": "duration_multiplier", + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted", + "message": "to_timezone is correct only at the materialization boundary -- the target zone lives in the ibis output dtype, not in the engine (SQL is a bare CAST AS TIMESTAMPTZ), so any expression composed on the result raises UnsupportedOperationError (verified 2026-07-29, ibis 12.0.0/duckdb)", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-07-29", "native_errors": [], "probe_exempt": null } @@ -48289,1113 +45297,1114 @@ { "backend": "ibis", "source": "mountainash", - "domain": "relation", + "domain": "datetime", "evidence": { - "probe_date": "2026-07-05", + "probe_date": "2026-08-16", "library_versions": [], - "fixtures": [] + "fixtures": [ + "ibis-sqlite", + "ibis-polars", + "narwhals-polars", + "narwhals-pandas" + ] }, "facts": [ { - "dialect": null, - "param": "resource", - "option_value": null, + "dialect": "ibis-polars", + "param": "unit", + "option_value": "1mo", "value_class": null, "level": "unsupported", - "enforcement": "router_metadata", + "enforcement": "gate", "boundary": "build", - "condition": "resource.dialect.escape_char is set", - "message": "CSV dialect field 'escape_char' is not native-safe on this backend's reader — routed to the CsvSpec fallback reader", - "workaround": "none needed — mountainash routes automatically", + "condition": null, + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-08-16", "native_errors": [], - "probe_exempt": "router, not gate — fallback handles it; behaviour covered by relations resource tests" + "probe_exempt": null }, { "dialect": "ibis-polars", - "param": "*", - "option_value": null, + "param": "unit", + "option_value": "1mo", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "with_row_index lowers to a window function (row_number); the ibis Polars backend has no WindowFunction translation rule.", - "workaround": "Use ibis-duckdb/ibis-sqlite, or polars/narwhals backends.", - "upstream_ref": "IB-REL-01", - "since": "2026-08-01", - "native_errors": [], - "probe_exempt": "relation op-level gap; covered by relation with_row_index cross-backend tests" - } - ] - }, - { - "backend": "ibis", - "source": "mountainash", - "domain": "set", - "evidence": null, - "facts": [ - { - "dialect": null, - "param": "haystack", - "option_value": null, - "value_class": null, - "level": "polymorphic", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "literal collections unwrap to raw values; expressions compile through (LIST-wrapper marker)", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-08-16", "native_errors": [], - "probe_exempt": "polymorphic — both paths supported by design" + "probe_exempt": null }, { - "dialect": null, - "param": "haystack", - "option_value": null, + "dialect": "ibis-polars", + "param": "unit", + "option_value": "1q", "value_class": null, - "level": "polymorphic", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "literal collections unwrap to raw values; expressions compile through (LIST-wrapper marker)", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-08-16", "native_errors": [], - "probe_exempt": "polymorphic — both paths supported by design" - } - ] - }, - { - "backend": "ibis", - "source": "mountainash", - "domain": "ternary", - "evidence": null, - "facts": [ + "probe_exempt": null + }, { - "dialect": null, - "param": "*", - "option_value": null, + "dialect": "ibis-polars", + "param": "unit", + "option_value": "1q", "value_class": null, - "level": "polymorphic", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "literal collections unwrap to raw values; expressions compile through (LIST-wrapper marker)", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-08-16", "native_errors": [], - "probe_exempt": "polymorphic — both paths supported by design" - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "arithmetic", - "evidence": { - "probe_date": "2026-07-21", - "library_versions": [], - "fixtures": [ - "polars", - "ibis-duckdb", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ + "probe_exempt": null + }, { - "dialect": null, - "param": "division_type", - "option_value": "FLOOR", + "dialect": "ibis-polars", + "param": "unit", + "option_value": "1y", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "division_type", - "option_value": "TRUNCATE", + "dialect": "ibis-polars", + "param": "unit", + "option_value": "1y", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_division_by_zero", - "option_value": "ERROR", + "dialect": "ibis-polars", + "param": "unit", + "option_value": "month", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_division_by_zero", - "option_value": "IEEE", + "dialect": "ibis-polars", + "param": "unit", + "option_value": "month", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_division_by_zero", - "option_value": "LIMIT", + "dialect": "ibis-polars", + "param": "unit", + "option_value": "quarter", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_division_by_zero", - "option_value": "NULL", + "dialect": "ibis-polars", + "param": "unit", + "option_value": "quarter", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": "ibis-polars", + "param": "unit", + "option_value": "year", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": "ibis-polars", + "param": "unit", + "option_value": "year", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- round/ceil cannot compute the next calendar boundary (truncate/floor, which only need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1h", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1h", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1h", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1h", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1m", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1m", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "NAN", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1m", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "NAN", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1m", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "NAN", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1ms", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "NAN", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1ms", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "NAN", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1ms", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "NAN", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1ms", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "NAN", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1q", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "NULL", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1q", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "on_domain_error", - "option_value": "NULL", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1q", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1q", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait i64 power overflow mode", - "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1s", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1s", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1s", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1s", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1us", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1us", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "ERROR", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1us", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SATURATE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "1us", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait i64 power overflow mode", - "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SATURATE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "hour", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SATURATE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "hour", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SATURATE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "hour", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SATURATE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "hour", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SATURATE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "microsecond", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SATURATE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "microsecond", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SATURATE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "microsecond", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SILENT", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "microsecond", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait i64 power overflow mode", - "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SILENT", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "millisecond", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SILENT", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "millisecond", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SILENT", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "millisecond", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SILENT", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "millisecond", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SILENT", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "minute", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SILENT", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "minute", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "overflow", - "option_value": "SILENT", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "minute", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "minute", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "quarter", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "quarter", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "quarter", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "quarter", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "second", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "second", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "second", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "second", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "ibis", + "source": "mountainash", + "domain": "relation", + "evidence": { + "probe_date": "2026-07-05", + "library_versions": [], + "fixtures": [] + }, + "facts": [ { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "resource", + "option_value": null, "value_class": null, "level": "unsupported", - "enforcement": "gate", + "enforcement": "router_metadata", "boundary": "build", - "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "condition": "resource.dialect.escape_char is set", + "message": "CSV dialect field 'escape_char' is not native-safe on this backend's reader — routed to the CsvSpec fallback reader", + "workaround": "none needed — mountainash routes automatically", "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": null + "probe_exempt": "router, not gate — fallback handles it; behaviour covered by relations resource tests" }, { - "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "dialect": "ibis-polars", + "param": "*", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", - "upstream_ref": null, - "since": "2026-07-21", + "message": "with_row_index lowers to a window function (row_number); the ibis Polars backend has no WindowFunction translation rule.", + "workaround": "Use ibis-duckdb/ibis-sqlite, or polars/narwhals backends.", + "upstream_ref": "IB-REL-01", + "since": "2026-08-01", "native_errors": [], - "probe_exempt": null - }, + "probe_exempt": "relation op-level gap; covered by relation with_row_index cross-backend tests" + } + ] + }, + { + "backend": "ibis", + "source": "mountainash", + "domain": "set", + "evidence": null, + "facts": [ { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "haystack", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "polymorphic", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "literal collections unwrap to raw values; expressions compile through (LIST-wrapper marker)", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": null + "probe_exempt": "polymorphic — both paths supported by design" }, { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "haystack", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "polymorphic", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "literal collections unwrap to raw values; expressions compile through (LIST-wrapper marker)", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": null - }, + "probe_exempt": "polymorphic — both paths supported by design" + } + ] + }, + { + "backend": "ibis", + "source": "mountainash", + "domain": "ternary", + "evidence": null, + "facts": [ { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "*", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "polymorphic", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "literal collections unwrap to raw values; expressions compile through (LIST-wrapper marker)", + "workaround": null, "upstream_ref": null, - "since": "2026-07-21", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": null - }, + "probe_exempt": "polymorphic — both paths supported by design" + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "arithmetic", + "evidence": { + "probe_date": "2026-07-21", + "library_versions": [], + "fixtures": [ + "polars", + "ibis-duckdb", + "narwhals-polars", + "narwhals-pandas" + ] + }, + "facts": [ { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "division_type", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49403,15 +46412,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "division_type", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49419,15 +46428,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "on_division_by_zero", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49435,15 +46444,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "on_division_by_zero", + "option_value": "IEEE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49451,15 +46460,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "on_division_by_zero", + "option_value": "LIMIT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49467,15 +46476,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "on_division_by_zero", + "option_value": "NULL", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49483,15 +46492,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "CEILING", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49499,15 +46508,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49515,15 +46524,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49531,15 +46540,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49547,15 +46556,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49563,15 +46572,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49579,15 +46588,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49595,15 +46604,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49611,15 +46620,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49627,15 +46636,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49643,15 +46652,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49659,15 +46668,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49675,15 +46684,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49691,15 +46700,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49707,15 +46716,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49723,15 +46732,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NULL", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49739,15 +46748,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NULL", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49755,15 +46764,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait i64 power overflow mode", + "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49771,15 +46780,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49787,15 +46796,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49803,15 +46812,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49819,15 +46828,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49835,15 +46844,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49851,15 +46860,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49867,15 +46876,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49883,15 +46892,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait i64 power overflow mode", + "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49899,15 +46908,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49915,15 +46924,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49931,15 +46940,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49947,15 +46956,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49963,15 +46972,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49979,15 +46988,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -49995,15 +47004,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -50011,15 +47020,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait i64 power overflow mode", + "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -50027,15 +47036,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -50043,15 +47052,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -50059,15 +47068,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -50075,15 +47084,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -50091,15 +47100,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -50107,15 +47116,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -50123,15 +47132,15 @@ }, { "dialect": null, - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -50140,7 +47149,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50156,7 +47165,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50172,7 +47181,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50188,7 +47197,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50204,7 +47213,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50220,7 +47229,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50236,7 +47245,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50252,7 +47261,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50268,7 +47277,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50284,7 +47293,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50300,7 +47309,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50316,7 +47325,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50332,7 +47341,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50348,7 +47357,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50364,7 +47373,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50380,7 +47389,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50396,7 +47405,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50412,7 +47421,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50428,7 +47437,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50444,7 +47453,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50460,7 +47469,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50476,7 +47485,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50492,7 +47501,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50508,7 +47517,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50524,7 +47533,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50540,7 +47549,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50556,7 +47565,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50572,7 +47581,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50588,7 +47597,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50604,7 +47613,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50620,7 +47629,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50636,7 +47645,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50652,7 +47661,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50668,7 +47677,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50684,7 +47693,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50700,7 +47709,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50716,7 +47725,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50732,7 +47741,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50748,7 +47757,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50764,7 +47773,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50780,7 +47789,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50796,7 +47805,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50812,7 +47821,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50828,7 +47837,7 @@ { "dialect": null, "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -50842,761 +47851,761 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "division_type", - "option_value": "FLOOR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "division_type", - "option_value": "TRUNCATE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_division_by_zero", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_division_by_zero", - "option_value": "IEEE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_division_by_zero", - "option_value": "LIMIT", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_division_by_zero", - "option_value": "NULL", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "NAN", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "NAN", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "NAN", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "NAN", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "NAN", + "dialect": null, + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "NAN", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "NAN", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "NULL", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "on_domain_error", - "option_value": "NULL", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait arithmetic option semantics", - "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait i64 power overflow mode", - "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "ERROR", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SATURATE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait i64 power overflow mode", - "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SATURATE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SATURATE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SATURATE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SATURATE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SATURATE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SATURATE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SATURATE", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SILENT", + "dialect": null, + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait i64 power overflow mode", - "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SILENT", + "dialect": null, + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SILENT", + "dialect": null, + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SILENT", + "dialect": null, + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SILENT", + "dialect": null, + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SILENT", + "dialect": null, + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SILENT", + "dialect": null, + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "overflow", - "option_value": "SILENT", + "dialect": null, + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait integer overflow mode", - "workaround": "Cast operands to a wider integer dtype before the operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51610,9 +48619,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51626,9 +48635,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51642,9 +48651,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51658,9 +48667,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51674,9 +48683,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51690,9 +48699,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51706,9 +48715,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51722,9 +48731,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51738,9 +48747,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51754,9 +48763,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51770,9 +48779,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51786,9 +48795,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51802,9 +48811,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "rounding", - "option_value": "CEILING", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -51819,15 +48828,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "CEILING", + "param": "division_type", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -51835,15 +48844,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "CEILING", + "param": "division_type", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -51851,15 +48860,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "CEILING", + "param": "on_division_by_zero", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -51867,15 +48876,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "CEILING", + "param": "on_division_by_zero", + "option_value": "IEEE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -51883,15 +48892,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "CEILING", + "param": "on_division_by_zero", + "option_value": "LIMIT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -51899,15 +48908,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "CEILING", + "param": "on_division_by_zero", + "option_value": "NULL", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -51915,63 +48924,63 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "CEILING", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -51979,15 +48988,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -51995,15 +49004,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52011,15 +49020,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52027,15 +49036,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52043,15 +49052,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52059,15 +49068,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52075,15 +49084,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52091,15 +49100,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52107,15 +49116,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52123,15 +49132,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52139,15 +49148,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NAN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52155,15 +49164,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NULL", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52171,15 +49180,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "on_domain_error", + "option_value": "NULL", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait arithmetic option semantics", + "workaround": "Pre-handle invalid arithmetic inputs and select the requested result before evaluating the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52187,111 +49196,111 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "FLOOR", + "param": "overflow", + "option_value": "ERROR", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "ERROR", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The explicit option selects the native backend's existing behavior, so it is observably equivalent to omission and cannot discriminate", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The DuckDB omission path raises the exact exception required by the requested ERROR semantics, so the explicit option is equivalent" }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait i64 power overflow mode", + "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52299,15 +49308,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52315,15 +49324,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait i64 power overflow mode", + "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52331,15 +49340,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52347,15 +49356,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52363,15 +49372,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52379,15 +49388,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52395,15 +49404,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52411,15 +49420,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52427,15 +49436,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SATURATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52443,15 +49452,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait i64 power overflow mode", + "workaround": "Pre-check the i64 base and exponent and handle out-of-range powers before calling power()", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52459,15 +49468,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52475,15 +49484,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52491,15 +49500,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52507,15 +49516,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52523,15 +49532,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52539,15 +49548,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52555,15 +49564,15 @@ }, { "dialect": "ibis-duckdb", - "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "param": "overflow", + "option_value": "SILENT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement the requested Substrait IEEE rounding mode", - "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", + "message": "The native backend does not implement the requested Substrait integer overflow mode", + "workaround": "Cast operands to a wider integer dtype before the operation", "upstream_ref": null, "since": "2026-07-21", "native_errors": [], @@ -52572,7 +49581,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52588,7 +49597,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_AWAY_FROM_ZERO", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52604,7 +49613,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52620,7 +49629,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52636,7 +49645,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52652,7 +49661,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52668,7 +49677,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52684,7 +49693,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52700,7 +49709,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52716,7 +49725,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52732,7 +49741,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52748,7 +49757,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52764,7 +49773,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52780,7 +49789,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52796,7 +49805,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52812,7 +49821,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52828,7 +49837,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52844,7 +49853,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52860,7 +49869,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52876,7 +49885,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52892,7 +49901,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "CEILING", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52908,7 +49917,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52924,7 +49933,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TIE_TO_EVEN", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52940,7 +49949,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52956,7 +49965,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52972,7 +49981,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -52988,7 +49997,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53004,7 +50013,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53020,7 +50029,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53036,7 +50045,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53052,7 +50061,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53068,7 +50077,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53084,7 +50093,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53100,7 +50109,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53116,7 +50125,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53132,7 +50141,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53148,7 +50157,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53164,7 +50173,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53180,7 +50189,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53196,7 +50205,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53212,7 +50221,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53228,7 +50237,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "FLOOR", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53244,7 +50253,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53260,7 +50269,7 @@ { "dialect": "ibis-duckdb", "param": "rounding", - "option_value": "TRUNCATE", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -53272,2041 +50281,2024 @@ "since": "2026-07-21", "native_errors": [], "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "datetime", - "evidence": { - "probe_date": "2026-07-25", - "library_versions": [], - "fixtures": [ - "polars", - "ibis-duckdb", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ - { - "dialect": null, - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "assume_timezone silently drops the timezone (returns a naive timestamp) — the tz argument is ignored; only polars attaches the timezone", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-25", - "native_errors": [], - "probe_exempt": null - }, - { - "dialect": null, - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", - "level": "unsupported", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "local_timestamp returns the UTC wall clock, not the target-zone wall clock -- ibis has no timezone method and the naive re-cast discards the conversion (verified 2026-07-29, ibis 12.0.0/duckdb: 12:00 instead of 17:30 for Asia/Kolkata)", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-29", - "native_errors": [], - "probe_exempt": null }, { - "dialect": null, - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis has no timezone primitives; extract/extract_boolean's timezone option is silently ignored (the local component is read from the stored value, not the target zone) -- see capabilities/datetime/extract.py", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis has no timezone primitives; extract/extract_boolean's timezone option is silently ignored (the local component is read from the stored value, not the target zone) -- see capabilities/datetime/extract.py", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "strptime_timestamp silently drops the timezone (returns a naive timestamp) on ibis -- ibis has no timezone primitives, matching assume_timezone/to_timezone/local_timestamp/extract.timezone", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "assume_timezone silently drops the timezone (returns a naive timestamp) — the tz argument is ignored; only polars attaches the timezone", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "local_timestamp returns the UTC wall clock, not the target-zone wall clock -- ibis has no timezone method and the naive re-cast discards the conversion (verified 2026-07-29, ibis 12.0.0/duckdb: 12:00 instead of 17:30 for Asia/Kolkata)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-29", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis has no timezone primitives; extract/extract_boolean's timezone option is silently ignored (the local component is read from the stored value, not the target zone) -- see capabilities/datetime/extract.py", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis has no timezone primitives; extract/extract_boolean's timezone option is silently ignored (the local component is read from the stored value, not the target zone) -- see capabilities/datetime/extract.py", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "timezone", - "option_value": null, - "value_class": "iana_timezone", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "strptime_timestamp silently drops the timezone (returns a naive timestamp) on ibis -- ibis has no timezone primitives, matching assume_timezone/to_timezone/local_timestamp/extract.timezone", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "datetime", - "evidence": { - "probe_date": "2026-07-30", - "library_versions": [ - [ - "ibis", - "12.0.0" - ], - [ - "narwhals", - "2.23.0" - ] - ], - "fixtures": [ - "polars", - "ibis-duckdb", - "ibis-polars", - "ibis-sqlite", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ + }, { - "dialect": "ibis-sqlite", - "param": "*", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-sqlite has no compilation rule for StringToDate/StringToTimestamp (OperationNotDefinedError); format-driven parsing is unavailable on this dialect, so it is gated rather than left to fail natively", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-30", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "whole-op gate on a WILDCARD_PARAM fact; cannot be keyed on an OpSpec param (OpSpecs are indexed by concrete argument name) — verified by the dedicated cross-backend gate tests in test_datetime_strptime_format.py" + "probe_exempt": null }, { - "dialect": "ibis-sqlite", - "param": "*", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-sqlite has no compilation rule for StringToDate/StringToTimestamp (OperationNotDefinedError); format-driven parsing is unavailable on this dialect, so it is gated rather than left to fail natively", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-30", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "whole-op gate on a WILDCARD_PARAM fact; cannot be keyed on an OpSpec param (OpSpecs are indexed by concrete argument name) — verified by the dedicated cross-backend gate tests in test_datetime_strptime_format.py" - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "datetime", - "evidence": { - "probe_date": "2026-08-15", - "library_versions": [ - [ - "polars", - "1.43.2" - ], - [ - "narwhals", - "2.24.0" - ], - [ - "ibis", - "12.0.0" - ] - ], - "fixtures": [ - "polars", - "ibis-duckdb", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ + "probe_exempt": null + }, { - "dialect": null, - "param": "component", - "option_value": "IS_DST", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "extract_boolean(IS_DST) is a placeholder (constant False) on all backends; deferred to backlog item 65 (is-dst-placeholder-implementation)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "component", - "option_value": "MONDAY_WEEK", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "component", - "option_value": "NANOSECOND", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "component", - "option_value": "PICOSECOND", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "component", - "option_value": "SUNDAY_WEEK", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "component", - "option_value": "TIMEZONE_OFFSET", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "component", - "option_value": "US_WEEK", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "component", - "option_value": "US_YEAR", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "component", - "option_value": "IS_DST", + "param": "rounding", + "option_value": "TIE_AWAY_FROM_ZERO", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "extract_boolean(IS_DST) is a placeholder (constant False) on all backends; deferred to backlog item 65 (is-dst-placeholder-implementation)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "component", - "option_value": "MONDAY_WEEK", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "component", - "option_value": "NANOSECOND", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "component", - "option_value": "PICOSECOND", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "component", - "option_value": "SUNDAY_WEEK", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "component", - "option_value": "TIMEZONE_OFFSET", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "component", - "option_value": "US_WEEK", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "component", - "option_value": "US_YEAR", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-08-15", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "string", - "evidence": { - "probe_date": "2026-07-05", - "library_versions": [], - "fixtures": [] - }, - "facts": [ + }, { - "dialect": null, - "param": "character", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", - "workaround": "Use a literal value, or the polars backend", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "dynamic arg silently miscompiles: str(Expr) bakes the unresolved expression's Python repr into the output as a literal string rather than raising — cannot be confirmed by an exception-based probe" + "probe_exempt": null }, { - "dialect": null, - "param": "characters", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", - "workaround": "Use a literal value, or the polars backend", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "dynamic arg silently miscompiles via str(Expr) into a no-op char-class, returning the input unchanged rather than raising — cannot be confirmed by an exception-based probe" + "probe_exempt": null }, { - "dialect": null, - "param": "characters", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", - "workaround": "Use a literal value, or the polars backend", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "dynamic arg silently miscompiles via str(Expr) into a no-op char-class, returning the input unchanged rather than raising — cannot be confirmed by an exception-based probe" + "probe_exempt": null }, { - "dialect": null, - "param": "characters", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", - "workaround": "Use a literal value, or the polars backend", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "dynamic arg silently miscompiles via str(Expr) into a no-op char-class, returning the input unchanged rather than raising — cannot be confirmed by an exception-based probe" + "probe_exempt": null }, { - "dialect": null, - "param": "length", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", - "workaround": "Use a literal value, or the polars backend", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "length", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", - "workaround": "Use a literal value, or the polars backend", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "replacement", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", - "workaround": "Use a literal value, or the polars backend", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "dynamic arg silently miscompiles: str(Expr) bakes the unresolved expression's Python repr into the output as a literal string rather than raising — cannot be confirmed by an exception-based probe" + "probe_exempt": null }, { - "dialect": null, - "param": "start", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", - "workaround": "Use a literal value, or the polars backend", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-05", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "string", - "evidence": { - "probe_date": "2026-07-23", - "library_versions": [], - "fixtures": [ - "polars", - "ibis-duckdb", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ + }, { - "dialect": null, - "param": "*", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "This string operation has no correct native implementation on this backend at the pinned floor; it is gated to fail loudly rather than return wrong data", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "whole-op gate; verified by the dedicated op-level probe suite (test_op_level_gate_probes.py), which cannot be keyed on an OpSpec param" + "probe_exempt": null }, { - "dialect": null, - "param": "*", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "This string operation has no correct native implementation on this backend at the pinned floor; it is gated to fail loudly rather than return wrong data", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "whole-op gate; verified by the dedicated op-level probe suite (test_op_level_gate_probes.py), which cannot be keyed on an OpSpec param" + "probe_exempt": null }, { - "dialect": null, - "param": "*", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "This string operation has no correct native implementation on this backend at the pinned floor; it is gated to fail loudly rather than return wrong data", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], - "probe_exempt": "whole-op gate; verified by the dedicated op-level probe suite (test_op_level_gate_probes.py), which cannot be keyed on an OpSpec param" + "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", - "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", - "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TIE_TO_EVEN", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", - "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", - "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "char_set", - "option_value": "UTF8", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "char_set", - "option_value": "UTF8", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "char_set", - "option_value": "UTF8", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "dotall", - "option_value": "DOTALL_DISABLED", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "dotall", - "option_value": "DOTALL_DISABLED", + "dialect": "ibis-duckdb", + "param": "rounding", + "option_value": "TRUNCATE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", - "workaround": null, + "message": "The native backend does not implement the requested Substrait IEEE rounding mode", + "workaround": "Evaluate with native rounding, then apply an explicit application-level numeric policy", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-21", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "datetime", + "evidence": { + "probe_date": "2026-07-25", + "library_versions": [], + "fixtures": [ + "polars", + "ibis-duckdb", + "narwhals-polars", + "narwhals-pandas" + ] + }, + "facts": [ { "dialect": null, - "param": "dotall", - "option_value": "DOTALL_DISABLED", - "value_class": null, + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "assume_timezone silently drops the timezone (returns a naive timestamp) — the tz argument is ignored; only polars attaches the timezone", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-25", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "dotall", - "option_value": "DOTALL_ENABLED", - "value_class": null, + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "local_timestamp returns the UTC wall clock, not the target-zone wall clock -- ibis has no timezone method and the naive re-cast discards the conversion (verified 2026-07-29, ibis 12.0.0/duckdb: 12:00 instead of 17:30 for Asia/Kolkata)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-29", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "dotall", - "option_value": "DOTALL_ENABLED", - "value_class": null, + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "ibis has no timezone primitives; extract/extract_boolean's timezone option is silently ignored (the local component is read from the stored value, not the target zone) -- see capabilities/datetime/extract.py", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "dotall", - "option_value": "DOTALL_ENABLED", - "value_class": null, + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "ibis has no timezone primitives; extract/extract_boolean's timezone option is silently ignored (the local component is read from the stored value, not the target zone) -- see capabilities/datetime/extract.py", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "dotall", - "option_value": "DOTALL_ENABLED", - "value_class": null, + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "strptime_timestamp silently drops the timezone (returns a naive timestamp) on ibis -- ibis has no timezone primitives, matching assume_timezone/to_timezone/local_timestamp/extract.timezone", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "dotall", - "option_value": "DOTALL_ENABLED", - "value_class": null, + "dialect": "ibis-duckdb", + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "assume_timezone silently drops the timezone (returns a naive timestamp) — the tz argument is ignored; only polars attaches the timezone", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-25", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "dotall", - "option_value": "DOTALL_ENABLED", - "value_class": null, + "dialect": "ibis-duckdb", + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "local_timestamp returns the UTC wall clock, not the target-zone wall clock -- ibis has no timezone method and the naive re-cast discards the conversion (verified 2026-07-29, ibis 12.0.0/duckdb: 12:00 instead of 17:30 for Asia/Kolkata)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-29", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "group", + "dialect": "ibis-duckdb", + "param": "timezone", "option_value": null, - "value_class": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis has no timezone primitives; extract/extract_boolean's timezone option is silently ignored (the local component is read from the stored value, not the target zone) -- see capabilities/datetime/extract.py", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], - "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" + "probe_exempt": null }, { - "dialect": null, - "param": "group", - "option_value": "2", - "value_class": null, + "dialect": "ibis-duckdb", + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis has no timezone primitives; extract/extract_boolean's timezone option is silently ignored (the local component is read from the stored value, not the target zone) -- see capabilities/datetime/extract.py", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "multiline", - "option_value": "MULTILINE_DISABLED", - "value_class": null, + "dialect": "ibis-duckdb", + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "strptime_timestamp silently drops the timezone (returns a naive timestamp) on ibis -- ibis has no timezone primitives, matching assume_timezone/to_timezone/local_timestamp/extract.timezone", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "datetime", + "evidence": { + "probe_date": "2026-07-30", + "library_versions": [ + [ + "ibis", + "12.0.0" + ], + [ + "narwhals", + "2.23.0" + ] + ], + "fixtures": [ + "polars", + "ibis-duckdb", + "ibis-polars", + "ibis-sqlite", + "narwhals-polars", + "narwhals-pandas" + ] + }, + "facts": [ { - "dialect": null, - "param": "multiline", - "option_value": "MULTILINE_DISABLED", + "dialect": "ibis-sqlite", + "param": "*", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis-sqlite has no compilation rule for StringToDate/StringToTimestamp (OperationNotDefinedError); format-driven parsing is unavailable on this dialect, so it is gated rather than left to fail natively", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-30", "native_errors": [], - "probe_exempt": null + "probe_exempt": "whole-op gate on a WILDCARD_PARAM fact; cannot be keyed on an OpSpec param (OpSpecs are indexed by concrete argument name) — verified by the dedicated cross-backend gate tests in test_datetime_strptime_format.py" }, { - "dialect": null, - "param": "multiline", - "option_value": "MULTILINE_DISABLED", + "dialect": "ibis-sqlite", + "param": "*", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis-sqlite has no compilation rule for StringToDate/StringToTimestamp (OperationNotDefinedError); format-driven parsing is unavailable on this dialect, so it is gated rather than left to fail natively", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-30", "native_errors": [], - "probe_exempt": null - }, + "probe_exempt": "whole-op gate on a WILDCARD_PARAM fact; cannot be keyed on an OpSpec param (OpSpecs are indexed by concrete argument name) — verified by the dedicated cross-backend gate tests in test_datetime_strptime_format.py" + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "datetime", + "evidence": { + "probe_date": "2026-08-15", + "library_versions": [ + [ + "polars", + "1.43.2" + ], + [ + "narwhals", + "2.24.0" + ], + [ + "ibis", + "12.0.0" + ] + ], + "fixtures": [ + "polars", + "ibis-duckdb", + "narwhals-polars", + "narwhals-pandas" + ] + }, + "facts": [ { "dialect": null, - "param": "multiline", - "option_value": "MULTILINE_ENABLED", + "param": "component", + "option_value": "IS_DST", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "extract_boolean(IS_DST) is a placeholder (constant False) on all backends; deferred to backlog item 65 (is-dst-placeholder-implementation)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "multiline", - "option_value": "MULTILINE_ENABLED", + "param": "component", + "option_value": "MONDAY_WEEK", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "multiline", - "option_value": "MULTILINE_ENABLED", + "param": "component", + "option_value": "NANOSECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "multiline", - "option_value": "MULTILINE_ENABLED", + "param": "component", + "option_value": "PICOSECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "multiline", - "option_value": "MULTILINE_ENABLED", + "param": "component", + "option_value": "SUNDAY_WEEK", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "multiline", - "option_value": "MULTILINE_ENABLED", + "param": "component", + "option_value": "TIMEZONE_OFFSET", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "negative_start", - "option_value": "ERROR", + "param": "component", + "option_value": "US_WEEK", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement non-default negative_start semantics for substring", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { "dialect": null, - "param": "negative_start", - "option_value": "LEFT_OF_BEGINNING", + "param": "component", + "option_value": "US_YEAR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement non-default negative_start semantics for substring", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "occurrence", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "component", + "option_value": "IS_DST", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", + "message": "extract_boolean(IS_DST) is a placeholder (constant False) on all backends; deferred to backlog item 65 (is-dst-placeholder-implementation)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], - "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" + "probe_exempt": null }, { - "dialect": null, - "param": "occurrence", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "component", + "option_value": "MONDAY_WEEK", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], - "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" + "probe_exempt": null }, { - "dialect": null, - "param": "occurrence", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "component", + "option_value": "NANOSECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], - "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" + "probe_exempt": null }, { - "dialect": null, - "param": "occurrence", - "option_value": "2", + "dialect": "ibis-duckdb", + "param": "component", + "option_value": "PICOSECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "occurrence", - "option_value": "2", + "dialect": "ibis-duckdb", + "param": "component", + "option_value": "SUNDAY_WEEK", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "occurrence", - "option_value": "2", + "dialect": "ibis-duckdb", + "param": "component", + "option_value": "TIMEZONE_OFFSET", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "padding", - "option_value": "LEFT", + "dialect": "ibis-duckdb", + "param": "component", + "option_value": "US_WEEK", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement LEFT padding semantics for center", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "position", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "component", + "option_value": "US_YEAR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", + "message": "the native backend has no primitive for this extract component (verified by semantic probe; see capabilities/datetime/extract.py)", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-15", "native_errors": [], - "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" - }, + "probe_exempt": null + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "datetime", + "evidence": { + "probe_date": "2026-08-16", + "library_versions": [], + "fixtures": [ + "ibis-sqlite", + "ibis-polars" + ] + }, + "facts": [ { - "dialect": null, - "param": "position", - "option_value": null, + "dialect": "ibis-polars", + "param": "unit", + "option_value": "MONTH", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- CEIL/ROUND_TIE_DOWN/ROUND_TIE_UP cannot compute the next calendar boundary; verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], - "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" + "probe_exempt": null }, { - "dialect": null, - "param": "position", - "option_value": null, + "dialect": "ibis-polars", + "param": "unit", + "option_value": "YEAR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis's polars sub-backend translates interval addition via polars.duration(), which has no months/years kwarg -- CEIL/ROUND_TIE_DOWN/ROUND_TIE_UP cannot compute the next calendar boundary; verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], - "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" + "probe_exempt": null }, { - "dialect": null, - "param": "position", - "option_value": null, + "dialect": "ibis-sqlite", + "param": "multiple", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis-sqlite has no TimestampBucket compilation rule -- multiple > 1 is unsupported for every unit; verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], - "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" + "probe_exempt": null }, { - "dialect": null, - "param": "position", - "option_value": null, + "dialect": "ibis-sqlite", + "param": "multiple", + "option_value": "3", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis-sqlite has no TimestampBucket compilation rule -- multiple > 1 is unsupported for every unit; verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], - "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" + "probe_exempt": null }, { - "dialect": null, - "param": "position", - "option_value": "2", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "HOUR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "position", - "option_value": "2", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "HOUR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "position", - "option_value": "2", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MICROSECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "position", - "option_value": "2", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MICROSECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "position", - "option_value": "2", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MILLISECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MILLISECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", - "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MINUTE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", - "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "MINUTE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", - "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "SECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", - "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", + "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": "ibis-sqlite", + "param": "unit", + "option_value": "SECOND", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "message": "ibis-sqlite TimestampTruncate has no support for units finer than DAY (HOUR/MINUTE/SECOND/MILLISECOND/MICROSECOND); verified 2026-08-16, ibis 12.0.0", "workaround": null, "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "string", + "evidence": { + "probe_date": "2026-07-05", + "library_versions": [], + "fixtures": [] + }, + "facts": [ { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": null, + "param": "character", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", + "workaround": "Use a literal value, or the polars backend", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": null + "probe_exempt": "dynamic arg silently miscompiles: str(Expr) bakes the unresolved expression's Python repr into the output as a literal string rather than raising — cannot be confirmed by an exception-based probe" }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": null, + "param": "characters", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", + "workaround": "Use a literal value, or the polars backend", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": null + "probe_exempt": "dynamic arg silently miscompiles via str(Expr) into a no-op char-class, returning the input unchanged rather than raising — cannot be confirmed by an exception-based probe" }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": null, + "param": "characters", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", + "workaround": "Use a literal value, or the polars backend", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": null + "probe_exempt": "dynamic arg silently miscompiles via str(Expr) into a no-op char-class, returning the input unchanged rather than raising — cannot be confirmed by an exception-based probe" }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": null, + "param": "characters", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", + "workaround": "Use a literal value, or the polars backend", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": null + "probe_exempt": "dynamic arg silently miscompiles via str(Expr) into a no-op char-class, returning the input unchanged rather than raising — cannot be confirmed by an exception-based probe" }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE", + "dialect": null, + "param": "length", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", - "workaround": null, + "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", + "workaround": "Use a literal value, or the polars backend", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "dialect": null, + "param": "length", + "option_value": null, "value_class": null, - "level": "expr_capable", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, + "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", + "workaround": "Use a literal value, or the polars backend", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "dialect": null, + "param": "replacement", + "option_value": null, "value_class": null, - "level": "expr_capable", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, + "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", + "workaround": "Use a literal value, or the polars backend", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": "dynamic arg silently miscompiles: str(Expr) bakes the unresolved expression's Python repr into the output as a literal string rather than raising — cannot be confirmed by an exception-based probe" }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "dialect": null, + "param": "start", + "option_value": null, "value_class": null, - "level": "expr_capable", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, + "message": "Ibis has no native equivalent; mountainash composes this operation from literal parameters — dynamic column parameters are unsupported", + "workaround": "Use a literal value, or the polars backend", "upstream_ref": null, - "since": "2026-07-23", + "since": "2026-07-05", "native_errors": [], - "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" - }, + "probe_exempt": null + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "string", + "evidence": { + "probe_date": "2026-07-23", + "library_versions": [], + "fixtures": [ + "polars", + "ibis-duckdb", + "narwhals-polars", + "narwhals-pandas" + ] + }, + "facts": [ { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "dialect": null, + "param": "*", + "option_value": null, "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", + "message": "This string operation has no correct native implementation on this backend at the pinned floor; it is gated to fail loudly rather than return wrong data", "workaround": null, "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": "whole-op gate; verified by the dedicated op-level probe suite (test_op_level_gate_probes.py), which cannot be keyed on an OpSpec param" }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "dialect": null, + "param": "*", + "option_value": null, "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", + "message": "This string operation has no correct native implementation on this backend at the pinned floor; it is gated to fail loudly rather than return wrong data", "workaround": null, "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": "whole-op gate; verified by the dedicated op-level probe suite (test_op_level_gate_probes.py), which cannot be keyed on an OpSpec param" }, { - "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "dialect": null, + "param": "*", + "option_value": null, "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", + "message": "This string operation has no correct native implementation on this backend at the pinned floor; it is gated to fail loudly rather than return wrong data", "workaround": null, "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": "whole-op gate; verified by the dedicated op-level probe suite (test_op_level_gate_probes.py), which cannot be keyed on an OpSpec param" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "option_value": "CASE_INSENSITIVE", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", + "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "option_value": "CASE_INSENSITIVE", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", + "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "option_value": "CASE_INSENSITIVE", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", + "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "option_value": "CASE_INSENSITIVE", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", + "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, "since": "2026-07-23", @@ -55314,15 +52306,15 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, "since": "2026-07-23", @@ -55330,15 +52322,15 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "case_sensitivity", - "option_value": "CASE_SENSITIVE", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, "since": "2026-07-23", @@ -55346,15 +52338,15 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, "since": "2026-07-23", @@ -55362,15 +52354,15 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, "since": "2026-07-23", @@ -55378,15 +52370,15 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, "since": "2026-07-23", @@ -55394,15 +52386,15 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_SENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, "since": "2026-07-23", @@ -55410,15 +52402,15 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_SENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, "since": "2026-07-23", @@ -55426,15 +52418,15 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "char_set", - "option_value": "ASCII_ONLY", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_SENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, "since": "2026-07-23", @@ -55442,57 +52434,57 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "char_set", - "option_value": "UTF8", + "option_value": "ASCII_ONLY", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate", + "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", "workaround": null, "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "char_set", - "option_value": "UTF8", + "option_value": "ASCII_ONLY", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate", + "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", "workaround": null, "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "char_set", - "option_value": "UTF8", + "option_value": "ASCII_ONLY", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate", + "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", "workaround": null, "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "char_set", - "option_value": "UTF8", + "option_value": "ASCII_ONLY", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -55506,9 +52498,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "char_set", - "option_value": "UTF8", + "option_value": "ASCII_ONLY", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -55522,9 +52514,9 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "char_set", - "option_value": "UTF8", + "option_value": "ASCII_ONLY", "value_class": null, "level": "unsupported", "enforcement": "gate", @@ -55538,55 +52530,55 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "dotall", - "option_value": "DOTALL_DISABLED", + "dialect": null, + "param": "char_set", + "option_value": "UTF8", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", + "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", "workaround": null, "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "dotall", - "option_value": "DOTALL_DISABLED", + "dialect": null, + "param": "char_set", + "option_value": "UTF8", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", + "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", "workaround": null, "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "dotall", - "option_value": "DOTALL_DISABLED", + "dialect": null, + "param": "char_set", + "option_value": "UTF8", "value_class": null, - "level": "expr_capable", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", + "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", "workaround": null, "upstream_ref": null, "since": "2026-07-23", "native_errors": [], - "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" + "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "dotall", "option_value": "DOTALL_DISABLED", "value_class": null, @@ -55602,7 +52594,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "dotall", "option_value": "DOTALL_DISABLED", "value_class": null, @@ -55618,7 +52610,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "dotall", "option_value": "DOTALL_DISABLED", "value_class": null, @@ -55634,7 +52626,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "dotall", "option_value": "DOTALL_ENABLED", "value_class": null, @@ -55650,7 +52642,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "dotall", "option_value": "DOTALL_ENABLED", "value_class": null, @@ -55666,7 +52658,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "dotall", "option_value": "DOTALL_ENABLED", "value_class": null, @@ -55682,7 +52674,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "dotall", "option_value": "DOTALL_ENABLED", "value_class": null, @@ -55698,7 +52690,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "dotall", "option_value": "DOTALL_ENABLED", "value_class": null, @@ -55714,7 +52706,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "dotall", "option_value": "DOTALL_ENABLED", "value_class": null, @@ -55730,7 +52722,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "group", "option_value": null, "value_class": null, @@ -55746,7 +52738,7 @@ "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "group", "option_value": "2", "value_class": null, @@ -55762,55 +52754,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "multiline", - "option_value": "MULTILINE_DISABLED", - "value_class": null, - "level": "expr_capable", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-23", - "native_errors": [], - "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" - }, - { - "dialect": "ibis-duckdb", - "param": "multiline", - "option_value": "MULTILINE_DISABLED", - "value_class": null, - "level": "expr_capable", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-23", - "native_errors": [], - "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" - }, - { - "dialect": "ibis-duckdb", - "param": "multiline", - "option_value": "MULTILINE_DISABLED", - "value_class": null, - "level": "expr_capable", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-23", - "native_errors": [], - "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" - }, - { - "dialect": "ibis-duckdb", + "dialect": null, "param": "multiline", "option_value": "MULTILINE_DISABLED", "value_class": null, @@ -55826,7 +52770,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "multiline", "option_value": "MULTILINE_DISABLED", "value_class": null, @@ -55842,7 +52786,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "multiline", "option_value": "MULTILINE_DISABLED", "value_class": null, @@ -55858,7 +52802,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "multiline", "option_value": "MULTILINE_ENABLED", "value_class": null, @@ -55874,7 +52818,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "multiline", "option_value": "MULTILINE_ENABLED", "value_class": null, @@ -55890,7 +52834,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "multiline", "option_value": "MULTILINE_ENABLED", "value_class": null, @@ -55906,7 +52850,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "multiline", "option_value": "MULTILINE_ENABLED", "value_class": null, @@ -55922,7 +52866,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "multiline", "option_value": "MULTILINE_ENABLED", "value_class": null, @@ -55938,7 +52882,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "multiline", "option_value": "MULTILINE_ENABLED", "value_class": null, @@ -55954,7 +52898,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "negative_start", "option_value": "ERROR", "value_class": null, @@ -55970,7 +52914,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "negative_start", "option_value": "LEFT_OF_BEGINNING", "value_class": null, @@ -55986,23 +52930,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "negative_start", - "option_value": "WRAP_FROM_END", - "value_class": null, - "level": "expr_capable", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "The builder default emits WRAP_FROM_END, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-23", - "native_errors": [], - "probe_exempt": "The builder default emits WRAP_FROM_END, so the explicit option is observably equivalent to omission and cannot discriminate" - }, - { - "dialect": "ibis-duckdb", + "dialect": null, "param": "occurrence", "option_value": null, "value_class": null, @@ -56018,7 +52946,7 @@ "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "occurrence", "option_value": null, "value_class": null, @@ -56034,7 +52962,7 @@ "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "occurrence", "option_value": null, "value_class": null, @@ -56050,7 +52978,7 @@ "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "occurrence", "option_value": "2", "value_class": null, @@ -56066,7 +52994,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "occurrence", "option_value": "2", "value_class": null, @@ -56082,7 +53010,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "occurrence", "option_value": "2", "value_class": null, @@ -56098,7 +53026,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "padding", "option_value": "LEFT", "value_class": null, @@ -56114,23 +53042,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", - "param": "padding", - "option_value": "RIGHT", - "value_class": null, - "level": "expr_capable", - "enforcement": "gate", - "boundary": "build", - "condition": null, - "message": "RIGHT is the builder default, so the explicit option is observably equivalent to omission and cannot discriminate", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-23", - "native_errors": [], - "probe_exempt": "RIGHT is the builder default, so the explicit option is observably equivalent to omission and cannot discriminate" - }, - { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": null, "value_class": null, @@ -56146,7 +53058,7 @@ "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": null, "value_class": null, @@ -56162,7 +53074,7 @@ "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": null, "value_class": null, @@ -56178,7 +53090,7 @@ "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": null, "value_class": null, @@ -56194,7 +53106,7 @@ "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": null, "value_class": null, @@ -56210,7 +53122,7 @@ "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": "2", "value_class": null, @@ -56226,7 +53138,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": "2", "value_class": null, @@ -56242,7 +53154,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": "2", "value_class": null, @@ -56258,7 +53170,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": "2", "value_class": null, @@ -56274,7 +53186,7 @@ "probe_exempt": null }, { - "dialect": "ibis-duckdb", + "dialect": null, "param": "position", "option_value": "2", "value_class": null, @@ -56288,29 +53200,9 @@ "since": "2026-07-23", "native_errors": [], "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "string", - "evidence": { - "probe_date": "2026-08-12", - "library_versions": [ - [ - "ibis", - "12.0.0" - ] - ], - "fixtures": [ - "ibis-sqlite", - "ibis-duckdb" - ] - }, - "facts": [ + }, { - "dialect": "ibis-sqlite", + "dialect": "ibis-duckdb", "param": "case_sensitivity", "option_value": "CASE_INSENSITIVE", "value_class": null, @@ -56318,15 +53210,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-sqlite's native LOWER()/UPPER() are ASCII-only (no ICU extension loaded); CASE_INSENSITIVE's Unicode-aware-lowercasing contract (e.g. Kelvin Sign U+212A -> 'k') is unavailable on this dialect alone in the Ibis family — gated unconditionally for every ibis-sqlite connection and every input (including purely-ASCII input, which SQLite's native LOWER() handles correctly, and any caller-supplied connection with a custom Unicode-aware LOWER()/UPPER() override loaded onto it) because the capability fact is keyed on (backend, dialect), a static identity, with no visibility into a specific connection's actual loaded extensions or a specific call's runtime string content", - "workaround": "Use CASE_INSENSITIVE_ASCII instead if ASCII-only folding is sufficient (genuinely honored on ibis-sqlite via native translate()); otherwise Unicode-normalize both operands in Python (e.g. str.lower()) and reissue the comparison as CASE_SENSITIVE (NOT CASE_INSENSITIVE — this dialect-scoped gate is unconditional and still rejects CASE_INSENSITIVE even after preprocessing), or run this expression against a different Ibis dialect (ibis-duckdb) instead of ibis-sqlite", + "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", + "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-sqlite", + "dialect": "ibis-duckdb", "param": "case_sensitivity", "option_value": "CASE_INSENSITIVE", "value_class": null, @@ -56334,15 +53226,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-sqlite's native LOWER()/UPPER() are ASCII-only (no ICU extension loaded); CASE_INSENSITIVE's Unicode-aware-lowercasing contract (e.g. Kelvin Sign U+212A -> 'k') is unavailable on this dialect alone in the Ibis family — gated unconditionally for every ibis-sqlite connection and every input (including purely-ASCII input, which SQLite's native LOWER() handles correctly, and any caller-supplied connection with a custom Unicode-aware LOWER()/UPPER() override loaded onto it) because the capability fact is keyed on (backend, dialect), a static identity, with no visibility into a specific connection's actual loaded extensions or a specific call's runtime string content", - "workaround": "Use CASE_INSENSITIVE_ASCII instead if ASCII-only folding is sufficient (genuinely honored on ibis-sqlite via native translate()); otherwise Unicode-normalize both operands in Python (e.g. str.lower()) and reissue the comparison as CASE_SENSITIVE (NOT CASE_INSENSITIVE — this dialect-scoped gate is unconditional and still rejects CASE_INSENSITIVE even after preprocessing), or run this expression against a different Ibis dialect (ibis-duckdb) instead of ibis-sqlite", + "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", + "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-sqlite", + "dialect": "ibis-duckdb", "param": "case_sensitivity", "option_value": "CASE_INSENSITIVE", "value_class": null, @@ -56350,2087 +53242,2203 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-sqlite's native LOWER()/UPPER() are ASCII-only (no ICU extension loaded); CASE_INSENSITIVE's Unicode-aware-lowercasing contract (e.g. Kelvin Sign U+212A -> 'k') is unavailable on this dialect alone in the Ibis family — gated unconditionally for every ibis-sqlite connection and every input (including purely-ASCII input, which SQLite's native LOWER() handles correctly, and any caller-supplied connection with a custom Unicode-aware LOWER()/UPPER() override loaded onto it) because the capability fact is keyed on (backend, dialect), a static identity, with no visibility into a specific connection's actual loaded extensions or a specific call's runtime string content", - "workaround": "Use CASE_INSENSITIVE_ASCII instead if ASCII-only folding is sufficient (genuinely honored on ibis-sqlite via native translate()); otherwise Unicode-normalize both operands in Python (e.g. str.lower()) and reissue the comparison as CASE_SENSITIVE (NOT CASE_INSENSITIVE — this dialect-scoped gate is unconditional and still rejects CASE_INSENSITIVE even after preprocessing), or run this expression against a different Ibis dialect (ibis-duckdb) instead of ibis-sqlite", + "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", + "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "string", - "evidence": { - "probe_date": "2026-08-12", - "library_versions": [ - [ - "ibis", - "12.0.0" - ], - [ - "narwhals", - "2.24.0" - ] - ], - "fixtures": [ - "polars", - "ibis-duckdb", - "ibis-polars", - "ibis-sqlite", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ + }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", - "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", + "message": "The native backend does not implement CASE_INSENSITIVE semantics for this Substrait string operation", + "workaround": "Lowercase the input and search operand explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", - "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", - "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", - "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_SENSITIVE", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_SENSITIVE", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": null, + "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_SENSITIVE", "value_class": null, - "level": "unsupported", + "level": "expr_capable", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", + "native_errors": [], + "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + }, + { + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_SENSITIVE", + "value_class": null, + "level": "expr_capable", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", + "native_errors": [], + "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + }, + { + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_SENSITIVE", + "value_class": null, + "level": "expr_capable", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", + "native_errors": [], + "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + }, + { + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_SENSITIVE", + "value_class": null, + "level": "expr_capable", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", + "native_errors": [], + "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + }, + { + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_SENSITIVE", + "value_class": null, + "level": "expr_capable", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", + "native_errors": [], + "probe_exempt": "The builder default emits CASE_SENSITIVE, so the explicit option is observably equivalent to omission and cannot discriminate" + }, + { + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_SENSITIVE", + "value_class": null, + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" }, { "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_SENSITIVE", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", - "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", + "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" }, { "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_SENSITIVE", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", - "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", + "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" }, { "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_SENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", - "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_SENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", - "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "option_value": "CASE_SENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "param": "char_set", + "option_value": "ASCII_ONLY", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "param": "char_set", + "option_value": "ASCII_ONLY", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "param": "char_set", + "option_value": "ASCII_ONLY", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The native backend does not implement ASCII_ONLY char_set semantics for this Substrait case operation", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "param": "char_set", + "option_value": "ASCII_ONLY", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { "dialect": "ibis-duckdb", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "param": "char_set", + "option_value": "ASCII_ONLY", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", + "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-polars", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "dialect": "ibis-duckdb", + "param": "char_set", + "option_value": "ASCII_ONLY", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars has no compilation rule for StringTranslate (OperationNotDefinedError); the ASCII-only fold CASE_INSENSITIVE_ASCII needs for contains/starts_with/ends_with is unavailable on this dialect, unlike ibis-duckdb/ibis-sqlite which both support .translate()", + "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-polars", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "dialect": "ibis-duckdb", + "param": "char_set", + "option_value": "UTF8", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars has no compilation rule for StringTranslate (OperationNotDefinedError); the ASCII-only fold CASE_INSENSITIVE_ASCII needs for contains/starts_with/ends_with is unavailable on this dialect, unlike ibis-duckdb/ibis-sqlite which both support .translate()", + "message": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": "ibis-polars", - "param": "case_sensitivity", - "option_value": "CASE_INSENSITIVE_ASCII", + "dialect": "ibis-duckdb", + "param": "char_set", + "option_value": "UTF8", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars has no compilation rule for StringTranslate (OperationNotDefinedError); the ASCII-only fold CASE_INSENSITIVE_ASCII needs for contains/starts_with/ends_with is unavailable on this dialect, unlike ibis-duckdb/ibis-sqlite which both support .translate()", + "message": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-08-12", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "string", - "evidence": { - "probe_date": "2026-08-12", - "library_versions": [ - [ - "ibis", - "12.0.0" - ], - [ - "polars", - "1.43.2" - ] - ], - "fixtures": [ - "ibis-polars" - ] - }, - "facts": [ + "probe_exempt": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate" + }, { - "dialect": "ibis-polars", - "param": "pattern", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "char_set", + "option_value": "UTF8", "value_class": null, - "level": "literal_only", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars compiles this to Polars' native str.replace()/str.replace_all(), which does not support a dynamic (column-valued) pattern argument (tracked upstream as PL-STR-01/PL-STR-02 for the raw polars backend; see backlog item 81)", - "workaround": "Use a literal string pattern instead of a column reference", - "upstream_ref": "PL-STR-02", - "since": "2026-08-12", + "message": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits UTF8, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": "ibis-polars", - "param": "substring", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "char_set", + "option_value": "UTF8", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars compiles this to Polars' native str.replace()/str.replace_all(), which does not support a dynamic (column-valued) pattern argument (tracked upstream as PL-STR-01/PL-STR-02 for the raw polars backend; see backlog item 81)", - "workaround": "Use a literal string pattern instead of a column reference", - "upstream_ref": "PL-STR-01", - "since": "2026-08-12", + "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "ibis-polars", - "param": "substring", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "char_set", + "option_value": "UTF8", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars compiles this to Polars' native str.replace()/str.replace_all(), which does not support a dynamic (column-valued) pattern argument (tracked upstream as PL-STR-01/PL-STR-02 for the raw polars backend; see backlog item 81)", - "workaround": "Use a literal string pattern instead of a column reference", - "upstream_ref": "PL-STR-02", - "since": "2026-08-12", + "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "string", - "evidence": { - "probe_date": "2026-08-12", - "library_versions": [ - [ - "ibis", - "12.0.0" - ], - [ - "polars", - "1.43.2" - ] - ], - "fixtures": [ - "ibis-polars", - "ibis-duckdb", - "ibis-sqlite" - ] - }, - "facts": [ + }, { - "dialect": "ibis-polars", - "param": "*", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "char_set", + "option_value": "UTF8", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars has no compilation rule for StringSQLLike (OperationNotDefinedError) for any pattern, literal or dynamic; ibis-duckdb/ibis-sqlite both translate LIKE to native SQL correctly", - "workaround": "Use ibis-duckdb/ibis-sqlite or a polars/narwhals backend for LIKE patterns", - "upstream_ref": "IB-STR-06", - "since": "2026-08-12", + "message": "The underlying case operation is unimplemented/incorrect on this backend (no-op or missing method), so char_set cannot be honored", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], - "probe_exempt": "whole-op gate on a dialect-scoped WILDCARD_PARAM fact; cannot be keyed on an OpSpec param — verified by the dedicated cross-backend gate test in test_pattern.py (TestLikeIbisPolarsGate) and the native-bypass self-healing probe in test_op_level_gate_probes.py (test_like_ibis_polars_native_still_broken)" + "probe_exempt": null }, { - "dialect": "ibis-polars", - "param": "pattern", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_DISABLED", "value_class": null, - "level": "literal_only", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars compiles this to Polars' native columnar-argument path, which raises Ibis's own UnsupportedArgumentError for a dynamic (column-valued) argument; a literal value works fine", - "workaround": "Use a literal string pattern/separator instead of a column reference", - "upstream_ref": "IB-STR-09", - "since": "2026-08-12", + "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": "ibis-polars", - "param": "separator", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_DISABLED", "value_class": null, - "level": "literal_only", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars compiles this to Polars' native columnar-argument path, which raises Ibis's own UnsupportedArgumentError for a dynamic (column-valued) argument; a literal value works fine", - "workaround": "Use a literal string pattern/separator instead of a column reference", - "upstream_ref": "IB-STR-10", - "since": "2026-08-12", + "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null - } - ] - }, - { - "backend": "ibis", - "source": "substrait", - "domain": "string", - "evidence": { - "probe_date": "2026-08-13", - "library_versions": [ - [ - "ibis", - "12.0.0" - ], - [ - "polars", - "1.43.2" - ], - [ - "narwhals", - "2.24.0" - ] - ], - "fixtures": [ - "ibis-duckdb", - "ibis-polars", - "ibis-sqlite", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ + "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" + }, { - "dialect": "ibis-polars", - "param": "pattern", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_DISABLED", "value_class": null, - "level": "literal_only", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-polars compiles regexp_string_split to Polars' native re_split, which raises Ibis's own IbisError for a dynamic (column-valued) pattern; a literal pattern works fine", - "workaround": "Use a literal string pattern instead of a column reference", - "upstream_ref": "IB-STR-13", - "since": "2026-08-13", + "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": "ibis-sqlite", - "param": "*", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_DISABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "ibis-sqlite has no compilation rule for RegexSplit (OperationNotDefinedError) for any pattern, literal or dynamic; ibis-duckdb translates it to native SQL correctly and ibis-polars supports a literal pattern", - "workaround": "Use ibis-duckdb, ibis-polars with a literal pattern, or a Polars backend for regex split", - "upstream_ref": "IB-STR-12", - "since": "2026-08-13", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], - "probe_exempt": "whole-op gate on a dialect-scoped WILDCARD_PARAM fact; cannot be keyed on an OpSpec param -- verified by a dedicated cross-backend gate test plus the native-bypass self-healing probe in test_op_level_gate_probes.py, mirroring item 83's TestLikeIbisPolarsGate / test_like_ibis_polars_native_still_broken" - } - ] - }, - { - "backend": "narwhals", - "source": "mountainash", - "domain": "datetime", - "evidence": { - "probe_date": "2026-07-05", - "library_versions": [], - "fixtures": [] - }, - "facts": [ + "probe_exempt": null + }, { - "dialect": null, - "param": "days", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_DISABLED", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Narwhals datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "NW-DT-01", - "since": "2026-07-05", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "hours", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_DISABLED", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Narwhals datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "NW-DT-01", - "since": "2026-07-05", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "microseconds", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_ENABLED", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Narwhals datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "NW-DT-01", - "since": "2026-07-05", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "milliseconds", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_ENABLED", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Narwhals datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "NW-DT-01", - "since": "2026-07-05", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "minutes", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_ENABLED", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Narwhals datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "NW-DT-01", - "since": "2026-07-05", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "months", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_ENABLED", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Narwhals datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "NW-DT-01", - "since": "2026-07-05", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "seconds", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_ENABLED", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Narwhals datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "NW-DT-01", - "since": "2026-07-05", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": null, - "param": "years", - "option_value": null, + "dialect": "ibis-duckdb", + "param": "dotall", + "option_value": "DOTALL_ENABLED", "value_class": null, - "level": "literal_only", + "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "Narwhals datetime offset operations require literal integer values", - "workaround": "Use a literal integer for the offset amount", - "upstream_ref": "NW-DT-01", - "since": "2026-07-05", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", + "workaround": null, + "upstream_ref": null, + "since": "2026-07-23", "native_errors": [], "probe_exempt": null - } - ] - }, - { - "backend": "narwhals", - "source": "mountainash", - "domain": "datetime", - "evidence": { - "probe_date": "2026-07-24", - "library_versions": [], - "fixtures": [ - "polars", - "ibis-duckdb", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ + }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1d", + "dialect": "ibis-duckdb", + "param": "group", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1d", + "dialect": "ibis-duckdb", + "param": "group", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1h", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_DISABLED", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1h", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_DISABLED", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1m", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_DISABLED", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits this regexp flag value, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1m", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_DISABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1mo", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_DISABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1mo", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_DISABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1ms", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_ENABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1ms", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_ENABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1q", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_ENABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1q", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_ENABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1s", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_ENABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1s", + "dialect": "ibis-duckdb", + "param": "multiline", + "option_value": "MULTILINE_ENABLED", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's non-default Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1us", + "dialect": "ibis-duckdb", + "param": "negative_start", + "option_value": "ERROR", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement non-default negative_start semantics for substring", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1us", + "dialect": "ibis-duckdb", + "param": "negative_start", + "option_value": "LEFT_OF_BEGINNING", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement non-default negative_start semantics for substring", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1w", + "dialect": "ibis-duckdb", + "param": "negative_start", + "option_value": "WRAP_FROM_END", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The builder default emits WRAP_FROM_END, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "The builder default emits WRAP_FROM_END, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1w", + "dialect": "ibis-duckdb", + "param": "occurrence", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1w", + "dialect": "ibis-duckdb", + "param": "occurrence", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1w", + "dialect": "ibis-duckdb", + "param": "occurrence", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1y", + "dialect": "ibis-duckdb", + "param": "occurrence", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "1y", + "dialect": "ibis-duckdb", + "param": "occurrence", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "day", + "dialect": "ibis-duckdb", + "param": "occurrence", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "day", + "dialect": "ibis-duckdb", + "param": "padding", + "option_value": "LEFT", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement LEFT padding semantics for center", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "hour", + "dialect": "ibis-duckdb", + "param": "padding", + "option_value": "RIGHT", "value_class": null, - "level": "unsupported", + "level": "expr_capable", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "RIGHT is the builder default, so the explicit option is observably equivalent to omission and cannot discriminate", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "RIGHT is the builder default, so the explicit option is observably equivalent to omission and cannot discriminate" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "hour", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "microsecond", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "microsecond", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "millisecond", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "millisecond", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], - "probe_exempt": null + "probe_exempt": "value-agnostic companion to the representative-value positional fact; the value-scoped disposition probe drives the native-path check" }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "minute", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "minute", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not honor the regexp position/occurrence/group option; it is silently ignored rather than applied", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "month", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "month", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "quarter", + "dialect": "ibis-duckdb", + "param": "position", + "option_value": "2", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The underlying regexp operation is unavailable on this dialect, so its option value cannot be honored", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-07-23", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "string", + "evidence": { + "probe_date": "2026-08-12", + "library_versions": [ + [ + "ibis", + "12.0.0" + ] + ], + "fixtures": [ + "ibis-sqlite", + "ibis-duckdb" + ] + }, + "facts": [ { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "quarter", + "dialect": "ibis-sqlite", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, + "message": "ibis-sqlite's native LOWER()/UPPER() are ASCII-only (no ICU extension loaded); CASE_INSENSITIVE's Unicode-aware-lowercasing contract (e.g. Kelvin Sign U+212A -> 'k') is unavailable on this dialect alone in the Ibis family — gated unconditionally for every ibis-sqlite connection and every input (including purely-ASCII input, which SQLite's native LOWER() handles correctly, and any caller-supplied connection with a custom Unicode-aware LOWER()/UPPER() override loaded onto it) because the capability fact is keyed on (backend, dialect), a static identity, with no visibility into a specific connection's actual loaded extensions or a specific call's runtime string content", + "workaround": "Use CASE_INSENSITIVE_ASCII instead if ASCII-only folding is sufficient (genuinely honored on ibis-sqlite via native translate()); otherwise Unicode-normalize both operands in Python (e.g. str.lower()) and reissue the comparison as CASE_SENSITIVE (NOT CASE_INSENSITIVE — this dialect-scoped gate is unconditional and still rejects CASE_INSENSITIVE even after preprocessing), or run this expression against a different Ibis dialect (ibis-duckdb) instead of ibis-sqlite", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "second", + "dialect": "ibis-sqlite", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, + "message": "ibis-sqlite's native LOWER()/UPPER() are ASCII-only (no ICU extension loaded); CASE_INSENSITIVE's Unicode-aware-lowercasing contract (e.g. Kelvin Sign U+212A -> 'k') is unavailable on this dialect alone in the Ibis family — gated unconditionally for every ibis-sqlite connection and every input (including purely-ASCII input, which SQLite's native LOWER() handles correctly, and any caller-supplied connection with a custom Unicode-aware LOWER()/UPPER() override loaded onto it) because the capability fact is keyed on (backend, dialect), a static identity, with no visibility into a specific connection's actual loaded extensions or a specific call's runtime string content", + "workaround": "Use CASE_INSENSITIVE_ASCII instead if ASCII-only folding is sufficient (genuinely honored on ibis-sqlite via native translate()); otherwise Unicode-normalize both operands in Python (e.g. str.lower()) and reissue the comparison as CASE_SENSITIVE (NOT CASE_INSENSITIVE — this dialect-scoped gate is unconditional and still rejects CASE_INSENSITIVE even after preprocessing), or run this expression against a different Ibis dialect (ibis-duckdb) instead of ibis-sqlite", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "second", + "dialect": "ibis-sqlite", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, + "message": "ibis-sqlite's native LOWER()/UPPER() are ASCII-only (no ICU extension loaded); CASE_INSENSITIVE's Unicode-aware-lowercasing contract (e.g. Kelvin Sign U+212A -> 'k') is unavailable on this dialect alone in the Ibis family — gated unconditionally for every ibis-sqlite connection and every input (including purely-ASCII input, which SQLite's native LOWER() handles correctly, and any caller-supplied connection with a custom Unicode-aware LOWER()/UPPER() override loaded onto it) because the capability fact is keyed on (backend, dialect), a static identity, with no visibility into a specific connection's actual loaded extensions or a specific call's runtime string content", + "workaround": "Use CASE_INSENSITIVE_ASCII instead if ASCII-only folding is sufficient (genuinely honored on ibis-sqlite via native translate()); otherwise Unicode-normalize both operands in Python (e.g. str.lower()) and reissue the comparison as CASE_SENSITIVE (NOT CASE_INSENSITIVE — this dialect-scoped gate is unconditional and still rejects CASE_INSENSITIVE even after preprocessing), or run this expression against a different Ibis dialect (ibis-duckdb) instead of ibis-sqlite", + "upstream_ref": null, + "since": "2026-08-12", + "native_errors": [], + "probe_exempt": null + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "string", + "evidence": { + "probe_date": "2026-08-12", + "library_versions": [ + [ + "ibis", + "12.0.0" + ], + [ + "narwhals", + "2.24.0" + ] + ], + "fixtures": [ + "polars", + "ibis-duckdb", + "ibis-polars", + "ibis-sqlite", + "narwhals-polars", + "narwhals-pandas" + ] + }, + "facts": [ + { + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", + "value_class": null, + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", + "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "week", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", + "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "week", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", + "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "week", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", + "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "week", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "year", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", - "param": "unit", - "option_value": "year", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1d", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1d", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1h", + "dialect": null, + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1h", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", + "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1m", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", + "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1m", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", + "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1mo", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, + "message": "The native backend does not implement CASE_INSENSITIVE_ASCII semantics for this Substrait string operation (same disposition as CASE_INSENSITIVE — neither case-fold value is wired here)", + "workaround": "Fold the input and search operand to ASCII lowercase explicitly before applying the case-sensitive operation", "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1mo", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1ms", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1ms", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1q", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1q", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1s", + "dialect": "ibis-duckdb", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "The native backend does not implement this regexp flag's CASE_INSENSITIVE_ASCII Substrait semantics", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1s", + "dialect": "ibis-polars", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-polars has no compilation rule for StringTranslate (OperationNotDefinedError); the ASCII-only fold CASE_INSENSITIVE_ASCII needs for contains/starts_with/ends_with is unavailable on this dialect, unlike ibis-duckdb/ibis-sqlite which both support .translate()", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1us", + "dialect": "ibis-polars", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-polars has no compilation rule for StringTranslate (OperationNotDefinedError); the ASCII-only fold CASE_INSENSITIVE_ASCII needs for contains/starts_with/ends_with is unavailable on this dialect, unlike ibis-duckdb/ibis-sqlite which both support .translate()", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1us", + "dialect": "ibis-polars", + "param": "case_sensitivity", + "option_value": "CASE_INSENSITIVE_ASCII", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "ibis-polars has no compilation rule for StringTranslate (OperationNotDefinedError); the ASCII-only fold CASE_INSENSITIVE_ASCII needs for contains/starts_with/ends_with is unavailable on this dialect, unlike ibis-duckdb/ibis-sqlite which both support .translate()", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "string", + "evidence": { + "probe_date": "2026-08-12", + "library_versions": [ + [ + "ibis", + "12.0.0" + ], + [ + "polars", + "1.43.2" + ] + ], + "fixtures": [ + "ibis-polars" + ] + }, + "facts": [ { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1w", + "dialect": "ibis-polars", + "param": "pattern", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "ibis-polars compiles this to Polars' native str.replace()/str.replace_all(), which does not support a dynamic (column-valued) pattern argument (tracked upstream as PL-STR-01/PL-STR-02 for the raw polars backend; see backlog item 81)", + "workaround": "Use a literal string pattern instead of a column reference", + "upstream_ref": "PL-STR-02", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1w", + "dialect": "ibis-polars", + "param": "substring", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "ibis-polars compiles this to Polars' native str.replace()/str.replace_all(), which does not support a dynamic (column-valued) pattern argument (tracked upstream as PL-STR-01/PL-STR-02 for the raw polars backend; see backlog item 81)", + "workaround": "Use a literal string pattern instead of a column reference", + "upstream_ref": "PL-STR-01", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1w", + "dialect": "ibis-polars", + "param": "substring", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "ibis-polars compiles this to Polars' native str.replace()/str.replace_all(), which does not support a dynamic (column-valued) pattern argument (tracked upstream as PL-STR-01/PL-STR-02 for the raw polars backend; see backlog item 81)", + "workaround": "Use a literal string pattern instead of a column reference", + "upstream_ref": "PL-STR-02", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "string", + "evidence": { + "probe_date": "2026-08-12", + "library_versions": [ + [ + "ibis", + "12.0.0" + ], + [ + "polars", + "1.43.2" + ] + ], + "fixtures": [ + "ibis-polars", + "ibis-duckdb", + "ibis-sqlite" + ] + }, + "facts": [ { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1w", + "dialect": "ibis-polars", + "param": "*", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "ibis-polars has no compilation rule for StringSQLLike (OperationNotDefinedError) for any pattern, literal or dynamic; ibis-duckdb/ibis-sqlite both translate LIKE to native SQL correctly", + "workaround": "Use ibis-duckdb/ibis-sqlite or a polars/narwhals backend for LIKE patterns", + "upstream_ref": "IB-STR-06", + "since": "2026-08-12", "native_errors": [], - "probe_exempt": null + "probe_exempt": "whole-op gate on a dialect-scoped WILDCARD_PARAM fact; cannot be keyed on an OpSpec param — verified by the dedicated cross-backend gate test in test_pattern.py (TestLikeIbisPolarsGate) and the native-bypass self-healing probe in test_op_level_gate_probes.py (test_like_ibis_polars_native_still_broken)" }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1y", + "dialect": "ibis-polars", + "param": "pattern", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "ibis-polars compiles this to Polars' native columnar-argument path, which raises Ibis's own UnsupportedArgumentError for a dynamic (column-valued) argument; a literal value works fine", + "workaround": "Use a literal string pattern/separator instead of a column reference", + "upstream_ref": "IB-STR-09", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "1y", + "dialect": "ibis-polars", + "param": "separator", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "ibis-polars compiles this to Polars' native columnar-argument path, which raises Ibis's own UnsupportedArgumentError for a dynamic (column-valued) argument; a literal value works fine", + "workaround": "Use a literal string pattern/separator instead of a column reference", + "upstream_ref": "IB-STR-10", + "since": "2026-08-12", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "ibis", + "source": "substrait", + "domain": "string", + "evidence": { + "probe_date": "2026-08-13", + "library_versions": [ + [ + "ibis", + "12.0.0" + ], + [ + "polars", + "1.43.2" + ], + [ + "narwhals", + "2.24.0" + ] + ], + "fixtures": [ + "ibis-duckdb", + "ibis-polars", + "ibis-sqlite", + "narwhals-polars", + "narwhals-pandas" + ] + }, + "facts": [ { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "day", + "dialect": "ibis-polars", + "param": "pattern", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "ibis-polars compiles regexp_string_split to Polars' native re_split, which raises Ibis's own IbisError for a dynamic (column-valued) pattern; a literal pattern works fine", + "workaround": "Use a literal string pattern instead of a column reference", + "upstream_ref": "IB-STR-13", + "since": "2026-08-13", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "day", + "dialect": "ibis-sqlite", + "param": "*", + "option_value": null, "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "ibis-sqlite has no compilation rule for RegexSplit (OperationNotDefinedError) for any pattern, literal or dynamic; ibis-duckdb translates it to native SQL correctly and ibis-polars supports a literal pattern", + "workaround": "Use ibis-duckdb, ibis-polars with a literal pattern, or a Polars backend for regex split", + "upstream_ref": "IB-STR-12", + "since": "2026-08-13", "native_errors": [], - "probe_exempt": null - }, + "probe_exempt": "whole-op gate on a dialect-scoped WILDCARD_PARAM fact; cannot be keyed on an OpSpec param -- verified by a dedicated cross-backend gate test plus the native-bypass self-healing probe in test_op_level_gate_probes.py, mirroring item 83's TestLikeIbisPolarsGate / test_like_ibis_polars_native_still_broken" + } + ] + }, + { + "backend": "narwhals", + "source": "mountainash", + "domain": "datetime", + "evidence": { + "probe_date": "2026-07-05", + "library_versions": [], + "fixtures": [] + }, + "facts": [ { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "hour", + "dialect": null, + "param": "days", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "Narwhals datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "NW-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "hour", + "dialect": null, + "param": "hours", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "Narwhals datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "NW-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "microsecond", + "dialect": null, + "param": "microseconds", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "Narwhals datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "NW-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "microsecond", + "dialect": null, + "param": "milliseconds", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "Narwhals datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "NW-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "millisecond", + "dialect": null, + "param": "minutes", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "Narwhals datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "NW-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "millisecond", + "dialect": null, + "param": "months", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "Narwhals datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "NW-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "minute", + "dialect": null, + "param": "seconds", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "Narwhals datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "NW-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", - "param": "unit", - "option_value": "minute", + "dialect": null, + "param": "years", + "option_value": null, "value_class": null, - "level": "unsupported", + "level": "literal_only", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", - "workaround": null, - "upstream_ref": null, - "since": "2026-07-24", + "message": "Narwhals datetime offset operations require literal integer values", + "workaround": "Use a literal integer for the offset amount", + "upstream_ref": "NW-DT-01", + "since": "2026-07-05", "native_errors": [], "probe_exempt": null - }, + } + ] + }, + { + "backend": "narwhals", + "source": "mountainash", + "domain": "datetime", + "evidence": { + "probe_date": "2026-08-16", + "library_versions": [], + "fixtures": [ + "ibis-sqlite", + "ibis-polars", + "narwhals-polars", + "narwhals-pandas" + ] + }, + "facts": [ { - "dialect": "narwhals-polars", + "dialect": "narwhals-pandas", "param": "unit", - "option_value": "month", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "narwhals-pandas", "param": "unit", - "option_value": "month", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "narwhals-pandas", "param": "unit", - "option_value": "quarter", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "narwhals-pandas", "param": "unit", - "option_value": "quarter", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "narwhals-pandas", "param": "unit", - "option_value": "second", + "option_value": "week", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "narwhals-pandas", "param": "unit", - "option_value": "second", + "option_value": "week", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "narwhals-pandas", "param": "unit", "option_value": "week", "value_class": null, @@ -58438,15 +55446,15 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-polars", + "dialect": "narwhals-pandas", "param": "unit", "option_value": "week", "value_class": null, @@ -58454,160 +55462,157 @@ "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { "dialect": "narwhals-polars", "param": "unit", - "option_value": "week", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { "dialect": "narwhals-polars", "param": "unit", - "option_value": "week", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { "dialect": "narwhals-polars", "param": "unit", - "option_value": "year", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { "dialect": "narwhals-polars", "param": "unit", - "option_value": "year", + "option_value": "1w", "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-24", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null - } - ] - }, - { - "backend": "narwhals", - "source": "mountainash", - "domain": "datetime", - "evidence": { - "probe_date": "2026-07-25", - "library_versions": [], - "fixtures": [ - "polars", - "ibis-duckdb", - "narwhals-polars", - "narwhals-pandas" - ] - }, - "facts": [ + }, { - "dialect": "narwhals-pandas", + "dialect": "narwhals-polars", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", + "option_value": "week", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { - "dialect": "narwhals-pandas", + "dialect": "narwhals-polars", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", + "option_value": "week", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { "dialect": "narwhals-polars", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", + "option_value": "week", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null }, { "dialect": "narwhals-polars", "param": "unit", - "option_value": null, - "value_class": "duration_multiplier", + "option_value": "week", + "value_class": null, "level": "unsupported", "enforcement": "gate", "boundary": "build", "condition": null, - "message": "narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result", + "message": "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects", "workaround": null, "upstream_ref": null, - "since": "2026-07-25", + "since": "2026-08-16", "native_errors": [], "probe_exempt": null } ] }, + { + "backend": "narwhals", + "source": "mountainash", + "domain": "datetime", + "evidence": { + "probe_date": "2026-08-16", + "library_versions": [], + "fixtures": [ + "narwhals-polars", + "narwhals-pandas" + ] + }, + "facts": [] + }, { "backend": "narwhals", "source": "mountainash", diff --git a/docs/reference/expression-coverage.md b/docs/reference/expression-coverage.md index 94493fe1..c0c31b43 100644 --- a/docs/reference/expression-coverage.md +++ b/docs/reference/expression-coverage.md @@ -3,7 +3,7 @@ -Declarations: 42 · Facts: 1558 · Registered operations: 324 · Implementation records: 972 +Declarations: 43 · Facts: 1460 · Registered operations: 326 · Implementation records: 978 Scoped deviations (dialect/param/option/value-class) live in [`expression-coverage-scoped.md`](expression-coverage-scoped.md). @@ -50,9 +50,9 @@ Legend — cell states (by exception): | Backend | default_capable | audited_clean | constrained | NOT_IMPLEMENTED | UNKNOWN | ops_total | | --- | --- | --- | --- | --- | --- | --- | -| polars | 191 | 81 | 52 | 0 | 0 | 324 | -| narwhals | 97 | 149 | 78 | 0 | 0 | 324 | -| ibis | 138 | 112 | 74 | 0 | 0 | 324 | +| polars | 191 | 83 | 52 | 0 | 0 | 326 | +| narwhals | 97 | 151 | 78 | 0 | 0 | 326 | +| ibis | 138 | 112 | 76 | 0 | 0 | 326 | contradictions: 0 audited_unknown: 0 @@ -61,9 +61,9 @@ audited_unknown: 0 | Axis | Breakdown | | --- | --- | -| Level | expr_capable 149, literal_only 65, polymorphic 9, unsupported 1335 | -| Enforcement | gate 1551, router_metadata 3, materialize_residue 4 | -| Backend | polars 265, narwhals 662, ibis 631 | +| Level | expr_capable 149, literal_only 65, polymorphic 9, unsupported 1237 | +| Enforcement | gate 1453, router_metadata 3, materialize_residue 4 | +| Backend | polars 265, narwhals 586, ibis 609 | `pandas` / `pyarrow` are routed input types (they execute via the narwhals path) and are not independent coverage columns. @@ -72,8 +72,8 @@ audited_unknown: 0 | Backend | Source | Domain | Probe date | Library versions | Fixtures | | --- | --- | --- | --- | --- | --- | | ibis | mountainash | datetime | 2026-07-05 | | | -| ibis | mountainash | datetime | 2026-07-24 | | polars, ibis-duckdb, narwhals-polars, narwhals-pandas | -| ibis | mountainash | datetime | 2026-07-25 | | polars, ibis-duckdb, narwhals-polars, narwhals-pandas | +| ibis | mountainash | datetime | 2026-08-16 | | ibis-duckdb | +| ibis | mountainash | datetime | 2026-08-16 | | ibis-sqlite, ibis-polars, narwhals-polars, narwhals-pandas | | ibis | mountainash | relation | 2026-07-05 | | | | ibis | mountainash | set | — | — | — | | ibis | mountainash | ternary | — | — | — | @@ -81,6 +81,7 @@ audited_unknown: 0 | ibis | substrait | datetime | 2026-07-25 | | polars, ibis-duckdb, narwhals-polars, narwhals-pandas | | ibis | substrait | datetime | 2026-07-30 | ibis 12.0.0, narwhals 2.23.0 | polars, ibis-duckdb, ibis-polars, ibis-sqlite, narwhals-polars, narwhals-pandas | | ibis | substrait | datetime | 2026-08-15 | polars 1.43.2, narwhals 2.24.0, ibis 12.0.0 | polars, ibis-duckdb, narwhals-polars, narwhals-pandas | +| ibis | substrait | datetime | 2026-08-16 | | ibis-sqlite, ibis-polars | | ibis | substrait | string | 2026-07-05 | | | | ibis | substrait | string | 2026-07-23 | | polars, ibis-duckdb, narwhals-polars, narwhals-pandas | | ibis | substrait | string | 2026-08-12 | ibis 12.0.0 | ibis-sqlite, ibis-duckdb | @@ -89,8 +90,8 @@ audited_unknown: 0 | ibis | substrait | string | 2026-08-12 | ibis 12.0.0, polars 1.43.2 | ibis-polars, ibis-duckdb, ibis-sqlite | | ibis | substrait | string | 2026-08-13 | ibis 12.0.0, polars 1.43.2, narwhals 2.24.0 | ibis-duckdb, ibis-polars, ibis-sqlite, narwhals-polars, narwhals-pandas | | narwhals | mountainash | datetime | 2026-07-05 | | | -| narwhals | mountainash | datetime | 2026-07-24 | | polars, ibis-duckdb, narwhals-polars, narwhals-pandas | -| narwhals | mountainash | datetime | 2026-07-25 | | polars, ibis-duckdb, narwhals-polars, narwhals-pandas | +| narwhals | mountainash | datetime | 2026-08-16 | | ibis-sqlite, ibis-polars, narwhals-polars, narwhals-pandas | +| narwhals | mountainash | datetime | 2026-08-16 | | narwhals-polars, narwhals-pandas | | narwhals | mountainash | list | 2026-07-05 | | | | narwhals | mountainash | relation | 2026-07-05 | | | | narwhals | mountainash | set | — | — | — | @@ -134,7 +135,7 @@ audited_unknown: 0 | `ADD_MONTHS` | ✓ | ◐ partial (1 params, 0 option-selectors, 0 value-classes, 0 dialects) | ◐ partial (1 params, 0 option-selectors, 0 value-classes, 0 dialects) | | `ADD_SECONDS` | ✓ | ◐ partial (1 params, 0 option-selectors, 0 value-classes, 0 dialects) | ◐ partial (1 params, 0 option-selectors, 0 value-classes, 0 dialects) | | `ADD_YEARS` | ✓ | ◐ partial (1 params, 0 option-selectors, 0 value-classes, 0 dialects) | ◐ partial (1 params, 0 option-selectors, 0 value-classes, 0 dialects) | -| `CEIL` | ✓ | ◐ partial (1 params, 20 option-selectors, 1 value-classes, 2 dialects) | ◐ partial (1 params, 20 option-selectors, 1 value-classes, 1 dialects) | +| `CEIL` | ✓ | ◐ partial (1 params, 2 option-selectors, 0 value-classes, 2 dialects) | ◐ partial (1 params, 16 option-selectors, 0 value-classes, 2 dialects) | | `DATE` | ✓ | ✓ audited | ✓ audited | | `DAYS_IN_MONTH` | ✓ | ✓ audited | ✓ audited | | `DIFF_DAYS` | ✓ | ✓ audited | ✓ audited | @@ -160,14 +161,14 @@ audited_unknown: 0 | `EXTRACT_WEEK` | ✓ | ✓ audited | ✓ audited | | `EXTRACT_WEEKDAY` | ✓ | ✓ audited | ✓ audited | | `EXTRACT_YEAR` | ✓ | ✓ audited | ✓ audited | -| `FLOOR` | ✓ | ◐ partial (1 params, 2 option-selectors, 0 value-classes, 2 dialects) | ◐ partial (1 params, 2 option-selectors, 1 value-classes, 1 dialects) | +| `FLOOR` | ✓ | ◐ partial (1 params, 2 option-selectors, 0 value-classes, 2 dialects) | ◐ partial (1 params, 12 option-selectors, 0 value-classes, 1 dialects) | | `IS_DST` | ✓ | ✓ audited | ✓ audited | | `IS_LEAP_YEAR` | ✓ | ✓ audited | ✓ audited | | `MONTH_END` | ✓ | ✓ audited | ✓ audited | | `MONTH_START` | ✓ | ✓ audited | ✓ audited | | `NOW` | ✓ | ✓ audited | ✓ audited | | `OFFSET_BY` | ✓ | ✓ audited | ✓ audited | -| `ROUND` | ✓ | ◐ partial (1 params, 20 option-selectors, 1 value-classes, 2 dialects) | ◐ partial (1 params, 20 option-selectors, 1 value-classes, 1 dialects) | +| `ROUND` | ✓ | ◐ partial (1 params, 2 option-selectors, 0 value-classes, 2 dialects) | ◐ partial (1 params, 16 option-selectors, 0 value-classes, 2 dialects) | | `TIME` | ✓ | ✓ audited | ✓ audited | | `TODAY` | ✓ | ✓ audited | ✓ audited | | `TOTAL_DAYS` | ✓ | ✓ audited | ✓ audited | @@ -178,7 +179,7 @@ audited_unknown: 0 | `TOTAL_NANOSECONDS` | ✓ | ✓ audited | ✓ audited | | `TOTAL_SECONDS` | ✓ | ✓ audited | ✓ audited | | `TO_TIMEZONE` | ✓ | ✓ audited | ◐ partial (1 params, 0 option-selectors, 1 value-classes, 1 dialects) | -| `TRUNCATE` | ✓ | ◐ partial (1 params, 2 option-selectors, 0 value-classes, 2 dialects) | ◐ partial (1 params, 2 option-selectors, 1 value-classes, 1 dialects) | +| `TRUNCATE` | ✓ | ◐ partial (1 params, 2 option-selectors, 0 value-classes, 2 dialects) | ◐ partial (1 params, 12 option-selectors, 0 value-classes, 1 dialects) | ### `FKEY_MOUNTAINASH_SCALAR_LIST` (mountainash / list) @@ -323,6 +324,8 @@ audited_unknown: 0 | `EXTRACT` | ◐ partial (1 params, 6 option-selectors, 0 value-classes, 1 dialects) | ◐ partial (1 params, 9 option-selectors, 0 value-classes, 2 dialects) | ◐ partial (2 params, 7 option-selectors, 1 value-classes, 1 dialects) | | `EXTRACT_BOOLEAN` | ◐ partial (1 params, 1 option-selectors, 0 value-classes, 1 dialects) | ◐ partial (1 params, 1 option-selectors, 0 value-classes, 2 dialects) | ◐ partial (2 params, 1 option-selectors, 1 value-classes, 1 dialects) | | `LOCAL_TIMESTAMP` | ✓ audited | ✓ audited | ◐ partial (1 params, 0 option-selectors, 1 value-classes, 1 dialects) | +| `ROUND_CALENDAR` | ✓ audited | ✓ audited | ◐ partial (2 params, 8 option-selectors, 0 value-classes, 2 dialects) | +| `ROUND_TEMPORAL` | ✓ audited | ✓ audited | ◐ partial (2 params, 6 option-selectors, 0 value-classes, 1 dialects) | | `STRFTIME` | ✓ audited | ✓ audited | ✓ audited | | `STRPTIME_DATE` | ✓ audited | ◐ partial (0 params, 0 option-selectors, 0 value-classes, 1 dialects) · unsupported on narwhals-pandas | ◐ partial (0 params, 0 option-selectors, 0 value-classes, 1 dialects) · unsupported on ibis-sqlite | | `STRPTIME_TIMESTAMP` | ✓ audited | ✓ audited | ◐ partial (1 params, 0 option-selectors, 1 value-classes, 2 dialects) · unsupported on ibis-sqlite | diff --git a/src/mountainash/expressions/backends/capabilities/datetime/options.py b/src/mountainash/expressions/backends/capabilities/datetime/options.py index fe6ef392..656da6be 100644 --- a/src/mountainash/expressions/backends/capabilities/datetime/options.py +++ b/src/mountainash/expressions/backends/capabilities/datetime/options.py @@ -1,14 +1,31 @@ """Import-safe datetime option capability declarations. -Disposition matrix (verified by the controller Step-0 probe of all four -fixtures, post-Task-3a — authoritative): - -| op | polars | ibis (ibis-duckdb) | narwhals (each dialect) | -|-------------|--------|----------------------------|----------------------------| -| truncate | ALL | honor core+1w; declare 1q | honor core+1q; declare 1w | -| floor_dt | ALL | honor core+1w; declare 1q | honor core+1q; declare 1w | -| round_dt | ALL | declare EVERY value | declare EVERY value | -| ceil_dt | ALL | declare EVERY value | declare EVERY value | +Disposition matrix (item 74: truncate/round_dt/ceil_dt/floor_dt now redirect +through the real round_temporal/round_calendar implementation instead of a +silent-wrong truncate-fallback; re-verified 2026-08-16 against the pinned +libraries -- authoritative): + +| op | polars | ibis-duckdb | ibis-sqlite | ibis-polars | narwhals (each dialect) | +|-------------|--------|-------------|-------------------------|------------------|--------------------------| +| truncate | ALL | ALL | declare sub-day+quarter | ALL | declare 1w | +| floor_dt | ALL | ALL | declare sub-day+quarter | ALL | declare 1w | +| round_dt | ALL | ALL | declare sub-day+quarter | declare cal+qtr | declare 1w | +| ceil_dt | ALL | ALL | declare sub-day+quarter | declare cal+qtr | declare 1w | + +"declare sub-day+quarter" = {1h, 1m, 1s, 1ms, 1us, 1q} declared UNSUPPORTED +on ibis-sqlite for every op: sqlite's TimestampTruncate has no support for +units finer than DAY (blocks FLOOR itself, hence every op), and its +TimestampBucket has no compilation rule at all (blocks the multiple=3 +bucketing 1q needs, regardless of rounding mode). + +"declare cal+qtr" = {1y, 1mo, 1q} declared UNSUPPORTED on ibis-polars, but +ONLY for round_dt/ceil_dt: those need `floor + ibis.interval(months=..)`/ +`interval(years=..)` to compute the next boundary, and ibis's polars +sub-backend translates intervals via `polars.duration()`, which has no +months/years kwarg. truncate/floor_dt only ever call FLOOR (no interval +needed), so they honor 1y/1mo/1q on ibis-polars -- unlike ibis-sqlite's gap, +this one is genuinely rounding-mode-scoped, verified empirically (both +dialects' full op x value matrix probed directly, not inferred). Portable core = {1y, 1mo, 1d, 1h, 1m, 1s, 1ms, 1us}. 1ns was dropped in Task 3a (divisor-by-zero panic in polars). Honored cells carry NO fact — the cell @@ -16,8 +33,7 @@ fact; the visitor raises ``BackendCapabilityError`` BEFORE calling the backend (enforce_capabilities=True by default), so the silent narwhals-truncate fallback and the raw ibis error are both replaced by a -clean error path. Backend round/ceil/floor impls are NOT edited — the -capability facts are the only honesty mechanism. +clean error path. Friendly aliases (year, quarter, month, week, day, hour, minute, second, millisecond, microsecond) are normalized to their canonical duration form @@ -31,14 +47,17 @@ because the api builder already normalized it — but the integrity guard counts them as separate cells, so they must be separately declared. -Family / dialect separation (mirrors the string ``padding`` slice): - - ibis declared: family-default (dialect=None) fact AND ibis-duckdb fact; - ibis-duckdb-specific, but a dialect=None family default protects every - other ibis dialect (ibis-sqlite, ibis-bigquery, ...) from silently - re-accepting a value the family cannot honor. - - narwhals declared: per-dialect facts only (narwhals-polars AND - narwhals-pandas) — design-review I-1; NEVER a single dialect=None - narwhals family fact, which would conflate the two dialects. +Dialect scoping (item 74 revision): NO ibis family default (dialect=None). +ibis-duckdb honors every value; ibis-sqlite and ibis-polars each have real, +independent gaps (different units, different op subsets) discovered by +direct probing, not inference from one dialect. A shared family-default +baseline would either wrongly restrict duckdb or wrongly permit +sqlite/polars — concrete per-dialect facts for the two known-divergent +dialects is the honest choice (mirrors capabilities/datetime/rounding.py's +identical policy for the underlying Substrait round_temporal/round_calendar +ops in this same PR). narwhals declared facts remain per-dialect only +(narwhals-polars AND narwhals-pandas) — design-review I-1; NEVER a single +dialect=None narwhals family fact, which would conflate the two dialects. Migrated from mountainash.expressions.backends.expression_systems.datetime_option_capabilities (2026-08 capability-architecture PR). """ @@ -54,10 +73,10 @@ ) -_SINCE = "2026-07-24" +_SINCE = "2026-08-16" # Domain spelling must match MA_OPTION_DOMAINS — the test-side guard cross-references -# them. Post-Task-3a: 1ns/nanosecond were dropped; 1q (quarter) and 1w (week) remain +# them. 1ns/nanosecond were dropped (Task 3a); 1q (quarter) and 1w (week) remain # in the friendly input set but are declared per-backend by capability facts. _ALL_UNIT_VALUES = ("1y", "1mo", "1d", "1h", "1m", "1s", "1ms", "1us", "1w", "1q") _FRIENDLY_ALIASES: dict[str, str] = { @@ -65,43 +84,35 @@ "day": "1d", "hour": "1h", "minute": "1m", "second": "1s", "millisecond": "1ms", "microsecond": "1us", } -# Every value the disposition matrix enumerates: duration forms + friendly -# aliases. Both forms share the same per-backend disposition (the api -# builder normalizes aliases to the canonical form, so the backend only -# sees one or the other). -_ALL_VALUES_WITH_ALIASES: tuple[str, ...] = ( - _ALL_UNIT_VALUES + tuple(_FRIENDLY_ALIASES) -) -# Each (op, fkey) maps to the unit value set the visitor must gate. truncate -# and floor_dt share semantics (floor == truncate) per Task 3a; both honor -# core+1w on ibis (1q is not in ibis TimestampTruncate) and core+1q on -# narwhals (1w is not a supported narwhals truncate unit). round_dt and -# ceil_dt have NO native datetime impl on ibis/narwhals (silent -# truncate-fallback is an anti-pattern) — declared UNSUPPORTED for EVERY -# unit value on ibis and both narwhals dialects. +# Each op maps to the FKEY the visitor gates on. _UNIT_OP_FKEYS: dict[str, object] = { "truncate": FK_DT.TRUNCATE, "round_dt": FK_DT.ROUND, "ceil_dt": FK_DT.CEIL, "floor_dt": FK_DT.FLOOR, } +_ALL_FOUR_OPS = tuple(_UNIT_OP_FKEYS) + +# ibis-sqlite: every op declares the same set (TimestampTruncate has no +# sub-day support at all, which blocks FLOOR itself; TimestampBucket has no +# sqlite compilation rule, which blocks 1q's multiple=3 bucketing). +_IBIS_SQLITE_DECLARED: dict[str, tuple[str, ...]] = { + op: ("1h", "1m", "1s", "1ms", "1us", "1q") for op in _ALL_FOUR_OPS +} -# (op, value) pairs ibis-duckdb DECLARES UNSUPPORTED. (truncate and floor_dt: -# only 1q is undeclared on ibis; 1w is honored. round_dt and ceil_dt: ALL -# values are undeclared.) Keyed by duration form; the friendly-alias form -# is added at declaration time so the integrity guard sees both. -_IBIS_DUCKDB_DECLARED: dict[str, tuple[str, ...]] = { - "truncate": ("1q",), - "floor_dt": ("1q",), - "round_dt": _ALL_UNIT_VALUES, - "ceil_dt": _ALL_UNIT_VALUES, +# ibis-polars: ONLY round_dt/ceil_dt declare 1y/1mo/1q -- truncate/floor_dt +# (FLOOR-only, no interval addition) honor them. +_IBIS_POLARS_DECLARED: dict[str, tuple[str, ...]] = { + "round_dt": ("1y", "1mo", "1q"), + "ceil_dt": ("1y", "1mo", "1q"), } + +# narwhals: every op declares only 1w (dt.truncate rejects the '1w' duration +# on both dialects; round_dt/ceil_dt redirect through the same truncate-based +# implementation as truncate/floor_dt, so they inherit the identical gap). _NARWHALS_DECLARED: dict[str, tuple[str, ...]] = { - "truncate": ("1w",), - "floor_dt": ("1w",), - "round_dt": _ALL_UNIT_VALUES, - "ceil_dt": _ALL_UNIT_VALUES, + op: ("1w",) for op in _ALL_FOUR_OPS } @@ -125,40 +136,25 @@ def _declared_with_aliases( return out -# Per-(op, value) human-readable limitation messages, named after the real -# backend behavior the controller probe observed. -_IBIS_QUARTER_UNSUPPORTED = ( - "ibis TimestampTruncate rejects the quarter unit '1q' (and its " - "friendly alias 'quarter')" +_IBIS_SQLITE_MSG = ( + "ibis-sqlite has no TimestampTruncate support for units finer than DAY, " + "and no TimestampBucket compilation rule (blocks multiple>1 bucketing, " + "which quarter needs); verified 2026-08-16, ibis 12.0.0" ) -_IBIS_NO_NATIVE_ROUND_CEIL = ( - "ibis has no native datetime round/ceil; silently falling back to " - "truncate would return a wrong value" +_IBIS_POLARS_MSG = ( + "ibis's polars sub-backend translates interval addition via " + "polars.duration(), which has no months/years kwarg -- round/ceil " + "cannot compute the next calendar boundary (truncate/floor, which only " + "need FLOOR, are unaffected); verified 2026-08-16, ibis 12.0.0" ) _NARWHALS_TRUNCATE_WEEK_UNSUPPORTED = ( - "narwhals truncate rejects the week unit '1w' (and its friendly " - "alias 'week')" -) -_NARWHALS_NO_NATIVE_ROUND_CEIL = ( - "narwhals has no native datetime round/ceil; silently falling back to " - "truncate would return a wrong value" + "narwhals dt.truncate rejects the week unit '1w' (and its friendly " + "alias 'week') on both dialects" ) -def _ibis_duckdb_message(op: str, value: str) -> str: - if op in {"round_dt", "ceil_dt"}: - return _IBIS_NO_NATIVE_ROUND_CEIL - return _IBIS_QUARTER_UNSUPPORTED - - -def _narwhals_message(op: str, value: str) -> str: - if op in {"round_dt", "ceil_dt"}: - return _NARWHALS_NO_NATIVE_ROUND_CEIL - return _NARWHALS_TRUNCATE_WEEK_UNSUPPORTED - - -def _build_ibis_duckdb_facts() -> tuple[CapabilityFact, ...]: - declared = _declared_with_aliases(_IBIS_DUCKDB_DECLARED) +def _build_ibis_sqlite_facts() -> tuple[CapabilityFact, ...]: + declared = _declared_with_aliases(_IBIS_SQLITE_DECLARED) return tuple( CapabilityFact( operation_key=_UNIT_OP_FKEYS[op], @@ -166,8 +162,8 @@ def _build_ibis_duckdb_facts() -> tuple[CapabilityFact, ...]: option_value=value, level=CapabilityLevel.UNSUPPORTED, backend=CONST_BACKEND.IBIS, - dialect="ibis-duckdb", - message=_ibis_duckdb_message(op, value), + dialect="ibis-sqlite", + message=_IBIS_SQLITE_MSG, since=_SINCE, ) for op, declared_values in declared.items() @@ -175,12 +171,8 @@ def _build_ibis_duckdb_facts() -> tuple[CapabilityFact, ...]: ) -def _build_ibis_family_defaults() -> tuple[CapabilityFact, ...]: - """ibis dialect=None family-default facts — protect every other ibis - dialect (ibis-sqlite, ibis-bigquery, …) from silently re-accepting a - value the family cannot honor. Mirrors _IBIS_FAMILY_DEFAULTS in the - string padding slice.""" - declared = _declared_with_aliases(_IBIS_DUCKDB_DECLARED) +def _build_ibis_polars_facts() -> tuple[CapabilityFact, ...]: + declared = _declared_with_aliases(_IBIS_POLARS_DECLARED) return tuple( CapabilityFact( operation_key=_UNIT_OP_FKEYS[op], @@ -188,8 +180,8 @@ def _build_ibis_family_defaults() -> tuple[CapabilityFact, ...]: option_value=value, level=CapabilityLevel.UNSUPPORTED, backend=CONST_BACKEND.IBIS, - dialect=None, - message=_ibis_duckdb_message(op, value), + dialect="ibis-polars", + message=_IBIS_POLARS_MSG, since=_SINCE, ) for op, declared_values in declared.items() @@ -207,7 +199,7 @@ def _build_narwhals_dialect_facts(dialect: str) -> tuple[CapabilityFact, ...]: level=CapabilityLevel.UNSUPPORTED, backend=CONST_BACKEND.NARWHALS, dialect=dialect, - message=_narwhals_message(op, value), + message=_NARWHALS_TRUNCATE_WEEK_UNSUPPORTED, since=_SINCE, ) for op, declared_values in declared.items() @@ -215,14 +207,13 @@ def _build_narwhals_dialect_facts(dialect: str) -> tuple[CapabilityFact, ...]: ) -_IBIS_DUCKDB_FACTS = _build_ibis_duckdb_facts() -_IBIS_FAMILY_DEFAULTS = _build_ibis_family_defaults() +_IBIS_FACTS = _build_ibis_sqlite_facts() + _build_ibis_polars_facts() _NARWHALS_POLARS_FACTS = _build_narwhals_dialect_facts("narwhals-polars") _NARWHALS_PANDAS_FACTS = _build_narwhals_dialect_facts("narwhals-pandas") -# polars honors EVERY value in the MA unit domain — no facts. The portable -# core + the divergent members (1w, 1q) all reach a real polars method. -# (1ns was dropped in Task 3a — the validator rejects it before the visitor.) +# polars honors EVERY value in the MA unit domain — no facts. +# ibis-duckdb honors EVERY value now that round_dt/ceil_dt redirect through +# the real round_temporal/round_calendar implementation — no facts. from mountainash.core.capabilities.declarations import ( # noqa: E402 @@ -233,18 +224,16 @@ def _build_narwhals_dialect_facts(dialect: str) -> tuple[CapabilityFact, ...]: ) _EVIDENCE = ProbeEvidence( - probe_date=_SINCE, # 2026-07-24 - library_versions=(), # not recorded in the original docstring - fixtures=( - "polars", "ibis-duckdb", "narwhals-polars", "narwhals-pandas", - ), + probe_date=_SINCE, + library_versions=(), + fixtures=("ibis-sqlite", "ibis-polars", "narwhals-polars", "narwhals-pandas"), ) DECLARATIONS = ( CapabilityDeclaration( backend=CONST_BACKEND.IBIS, domain=Domain.DATETIME, source=FactSource.MOUNTAINASH, - facts=_IBIS_FAMILY_DEFAULTS + _IBIS_DUCKDB_FACTS, + facts=_IBIS_FACTS, evidence=_EVIDENCE, ), CapabilityDeclaration( diff --git a/src/mountainash/expressions/backends/capabilities/datetime/value_classes_ma.py b/src/mountainash/expressions/backends/capabilities/datetime/value_classes_ma.py index 9560d7b5..1e48c0a7 100644 --- a/src/mountainash/expressions/backends/capabilities/datetime/value_classes_ma.py +++ b/src/mountainash/expressions/backends/capabilities/datetime/value_classes_ma.py @@ -1,41 +1,54 @@ """Import-safe value-class capability declarations (MA ops). -Reinstates integer unit multipliers >= 2 (e.g. "2d", "3h", "12mo") on the four -Mountainash datetime rounding ops (truncate / round_dt / ceil_dt / floor_dt), -and declares to_timezone unsupported on ibis. - -Probe matrix — DURATION_MULTIPLIER and IANA_TIMEZONE, all-fixtures: - -| op | polars | ibis (ibis-duckdb) | narwhals-polars | narwhals-pandas | -|-------------|---------|--------------------|-----------------|-----------------| -| truncate | honored | RAISED | honored | honored | -| floor_dt | honored | RAISED | honored | honored | -| round_dt | honored | RAISED | SILENTLY-WRONG | SILENTLY-WRONG | -| ceil_dt | honored | RAISED | SILENTLY-WRONG | SILENTLY-WRONG | -| to_timezone | honored | UNCOMPOSABLE | honored | honored | - -- ibis raises for EVERY multiplier on ALL four rounding ops: `TimestampTruncate` rejects - the Polars-style "" duration form (SignatureValidationError) — the - ibis impls pass the raw duration to `x.truncate(...)`. -- narwhals HONORS multipliers on truncate/floor_dt (native `dt.truncate` accepts - the duration) but SILENTLY TRUNCATES on round_dt/ceil_dt (no native datetime - round/ceil -> falls back to truncate, so "2d" rounds DOWN instead of to the - nearest 2-day boundary — a wrong value). -- polars honors every multiplier on all four ops -> NO fact. +item 74 revision: truncate/round_dt/ceil_dt/floor_dt now redirect through the +real round_temporal/round_calendar implementation instead of calling +`x.truncate("")`/silently falling back to truncate. This retires +the DURATION_MULTIPLIER-class facts that used to declare ibis-duckdb +UNSUPPORTED for every multiplied unit ("2d", "3h", "12mo", ...) on all four +ops, and narwhals UNSUPPORTED for round_dt/ceil_dt specifically -- BOTH +claims are now false (verified 2026-08-16, re-probed through the live +redirect, not source-reading): + +| op | polars | ibis-duckdb | ibis-sqlite | ibis-polars | narwhals (each dialect) | +|-------------|---------|-------------|-------------|-------------|--------------------------| +| truncate | honored | honored | RAISED | honored | honored | +| floor_dt | honored | honored | RAISED | honored | honored | +| round_dt | honored | honored | RAISED | honored | honored | +| ceil_dt | honored | honored | RAISED | honored | honored | +| to_timezone | honored | UNCOMPOSABLE| honored | honored | honored | + +- ibis-duckdb honors every multiplied unit on all four rounding ops now: + round_temporal/round_calendar's TimestampValue.bucket() (multiple > 1) + works for every unit on duckdb. +- ibis-sqlite has no TimestampBucket compilation rule at all -- every + multiplied unit on all four ops raises there (this is the SAME real gap + capabilities/datetime/rounding.py declares for the direct Substrait + round_temporal/round_calendar ops; this module declares it again for the + four MA-wrapper FKEYs the visitor actually gates the outer call on). +- ibis-polars honors every multiplied fixed-duration unit (days/hours/etc); + its genuine gap is calendar-unit (MONTH/YEAR) round/ceil, which is a + closed, exact-value gap already declared in capabilities/datetime/options.py + -- not a DURATION_MULTIPLIER-class (open-value) concern. +- narwhals HONORS every multiplied unit on all four ops now: round_dt/ceil_dt + redirect through the same real hand-rolled round_temporal/round_calendar + body as truncate/floor_dt (no more silent truncate-fallback). +- polars honors every multiplier on all four ops -> NO fact (unchanged). - to_timezone on ibis is correct ONLY at the result-materialization boundary (the target zone lives in the ibis output dtype, but SQL is a bare CAST AS TIMESTAMPTZ), so any expression composed on the result raises UnsupportedOperationError (UNCOMPOSABLE). Declared UNSUPPORTED so the capability gate raises BackendCapabilityError. + (Unchanged by item 74 -- unrelated to rounding.) A value-class gates soundly here because the api-builder validates parameters to their respective value-class domains (spec Section 3.2). -Family / dialect discipline (mirrors PR-C `capabilities/datetime/options.py`): - - ibis: family-default (dialect=None) fact AND ibis-duckdb fact — the - dialect=None default protects every other ibis dialect from silently - re-accepting an operation/multiplier the family cannot honor. - - narwhals: per-dialect facts ONLY (narwhals-polars AND narwhals-pandas) — - never a dialect=None narwhals family default. +Dialect discipline (item 74 revision): NO ibis family default (dialect=None) +for the rounding facts -- ibis-duckdb and ibis-polars both honor every +multiplier now; only ibis-sqlite has a real gap, so it gets its own concrete +fact (mirrors capabilities/datetime/options.py's identical policy change in +this same PR). to_timezone's family default is unchanged (still a genuine +ibis-wide limitation). narwhals: per-dialect facts remain the convention +where needed, but round_dt/ceil_dt no longer need any narwhals fact at all. Migrated from mountainash.expressions.backends.expression_systems.datetime_value_class_capabilities_ma (2026-08 capability-architecture PR). """ @@ -51,29 +64,19 @@ FKEY_MOUNTAINASH_SCALAR_DATETIME as FK_DT, ) -_SINCE = "2026-07-25" +_SINCE = "2026-08-16" -# op-name -> FKEY, for the four unit-rounding ops. -_ALL_ROUNDING = { - "truncate": FK_DT.TRUNCATE, - "round_dt": FK_DT.ROUND, - "ceil_dt": FK_DT.CEIL, - "floor_dt": FK_DT.FLOOR, -} +# Known residual gap (documented, not enforced by a fact -- see module +# docstring): ibis-sqlite's TimestampBucket has no compilation rule, so a +# multi-digit MA-wrapper duration string (e.g. dt.truncate("2d")) on +# ibis-sqlite raises a raw native OperationNotDefinedError rather than a +# clean BackendCapabilityError. A DURATION_MULTIPLIER-class fact for this +# would need a corresponding class-backed OptionCell, which the 4-fixture +# argument-type matrix cannot instantiate for ibis-sqlite (same structural +# limit documented in test_option_fact_integrity.py's +# _MATRIX_UNREACHABLE_DIALECT_FACTS) -- tracked as a backlog follow-up +# rather than adding an untested, unexercised fact here. -# ibis raises on multipliers for ALL four rounding ops. -_IBIS_MULTIPLIER_OPS = ("truncate", "round_dt", "ceil_dt", "floor_dt") -# narwhals only diverges (silent truncate) on round/ceil; truncate/floor honor. -_NARWHALS_MULTIPLIER_OPS = ("round_dt", "ceil_dt") - -_IBIS_MSG = ( - "ibis TimestampTruncate rejects Polars-style multiplier duration units " - "(e.g. '2d', '3h', '12mo'); only single bare units are accepted" -) -_NARWHALS_ROUND_CEIL_MSG = ( - "narwhals has no native datetime round/ceil; a multiplier value silently " - "falls back to truncate and returns a wrong (down-rounded) result" -) _TO_TIMEZONE_MSG = ( "to_timezone is correct only at the materialization boundary -- the " "target zone lives in the ibis output dtype, not in the engine (SQL is a " @@ -82,17 +85,6 @@ ) -def _mult_fact(op: str, backend, dialect: str | None, message: str) -> CapabilityFact: - return CapabilityFact( - operation_key=_ALL_ROUNDING[op], - param="unit", - value_class=ValueClass.DURATION_MULTIPLIER, - level=CapabilityLevel.UNSUPPORTED, - backend=backend, - dialect=dialect, - message=message, - since=_SINCE, - ) def _tz_fact(backend, dialect: str | None, message: str) -> CapabilityFact: @@ -109,18 +101,11 @@ def _tz_fact(backend, dialect: str | None, message: str) -> CapabilityFact: _IBIS_FACTS = tuple( - _mult_fact(op, CONST_BACKEND.IBIS, dialect, _IBIS_MSG) - for op in _IBIS_MULTIPLIER_OPS - for dialect in (None, "ibis-duckdb") # family default + duckdb -) + tuple( _tz_fact(CONST_BACKEND.IBIS, dialect, _TO_TIMEZONE_MSG) for dialect in (None, "ibis-duckdb") ) -_NARWHALS_FACTS = tuple( - _mult_fact(op, CONST_BACKEND.NARWHALS, dialect, _NARWHALS_ROUND_CEIL_MSG) - for op in _NARWHALS_MULTIPLIER_OPS - for dialect in ("narwhals-polars", "narwhals-pandas") # per-dialect only -) +# narwhals honors every multiplied unit on all four ops now -- no facts. +_NARWHALS_FACTS: tuple[CapabilityFact, ...] = () from mountainash.core.capabilities.declarations import ( # noqa: E402 @@ -131,11 +116,14 @@ def _tz_fact(backend, dialect: str | None, message: str) -> CapabilityFact: ) _EVIDENCE = ProbeEvidence( - probe_date=_SINCE, # 2026-07-25 - library_versions=(), # not recorded in the original docstring - fixtures=( - "polars", "ibis-duckdb", "narwhals-polars", "narwhals-pandas", - ), + probe_date=_SINCE, + library_versions=(), + fixtures=("ibis-duckdb",), +) +_NARWHALS_EVIDENCE = ProbeEvidence( + probe_date=_SINCE, + library_versions=(), + fixtures=("narwhals-polars", "narwhals-pandas"), ) DECLARATIONS = ( @@ -149,6 +137,6 @@ def _tz_fact(backend, dialect: str | None, message: str) -> CapabilityFact: backend=CONST_BACKEND.NARWHALS, domain=Domain.DATETIME, source=FactSource.MOUNTAINASH, facts=_NARWHALS_FACTS, - evidence=_EVIDENCE, + evidence=_NARWHALS_EVIDENCE, ), ) diff --git a/src/mountainash/expressions/backends/expression_systems/ibis/extensions_mountainash/expsys_ib_ext_ma_scalar_datetime.py b/src/mountainash/expressions/backends/expression_systems/ibis/extensions_mountainash/expsys_ib_ext_ma_scalar_datetime.py index 6708cc7a..e883925d 100644 --- a/src/mountainash/expressions/backends/expression_systems/ibis/extensions_mountainash/expsys_ib_ext_ma_scalar_datetime.py +++ b/src/mountainash/expressions/backends/expression_systems/ibis/extensions_mountainash/expsys_ib_ext_ma_scalar_datetime.py @@ -26,21 +26,6 @@ ) -# Ibis .truncate() expects a bare unit letter, not the Polars-style "" duration. -# Shared by truncate() and floor_dt() (floor == truncate for datetime). Units with no -# entry here (e.g. "1q") fall through unmapped and raise a native ibis error — declared -# UNSUPPORTED on ibis via capability facts. -_IBIS_TRUNCATE_UNIT_MAPPING = { - "1y": "Y", - "1mo": "M", - "1w": "W", - "1d": "D", - "1h": "h", - "1m": "m", - "1s": "s", - "1ms": "ms", - "1us": "us", -} class MountainAshIbisScalarDatetimeExpressionSystem(IbisBaseExpressionSystem, MountainAshScalarDatetimeExpressionSystemProtocol["IbisValueExpr"]): @@ -431,15 +416,12 @@ def truncate( Args: x: Datetime expression. - unit: Unit string (1d, 1h, Y, M, D, h, m, s, etc.). + unit: Unit string (1d, 1h, 1mo, 1y, etc.). Returns: Truncated datetime. """ - # Ibis truncate expects just the unit letter, not "1d" format - # Convert "1d" -> "D", "1h" -> "h", etc. - unit_mapped = _IBIS_TRUNCATE_UNIT_MAPPING.get(unit, unit) - return x.truncate(unit_mapped) + return self._round(x, "FLOOR", unit) def round_dt( self, @@ -451,16 +433,12 @@ def round_dt( Args: x: Datetime expression. - unit: Unit string. + unit: Unit string (1d, 1h, 1mo, 1y, etc.). Returns: Rounded datetime. - - Note: - Ibis may not have round. Falls back to truncate. """ - # Ibis doesn't have round - fallback to truncate - return x.truncate(unit) + return self._round(x, "ROUND_TIE_UP", unit) def ceil_dt( self, @@ -472,16 +450,12 @@ def ceil_dt( Args: x: Datetime expression. - unit: Unit string. + unit: Unit string (1d, 1h, 1mo, 1y, etc.). Returns: Ceiling datetime. - - Note: - Ibis doesn't have ceil. Falls back to truncate. """ - # Ibis doesn't have ceil - fallback to truncate - return x.truncate(unit) + return self._round(x, "CEIL", unit) def floor_dt( self, @@ -493,19 +467,28 @@ def floor_dt( Args: x: Datetime expression. - unit: Unit string. + unit: Unit string (1d, 1h, 1mo, 1y, etc.). Returns: Floor datetime. - - Note: - floor == truncate for datetime (both drop the sub-unit remainder), so - ibis floor is honored wherever truncate is. Applies the same Polars-style - duration -> ibis unit-letter mapping as truncate (round_dt/ceil_dt have no - native ibis impl and are declared UNSUPPORTED via capability facts instead). """ - unit_mapped = _IBIS_TRUNCATE_UNIT_MAPPING.get(unit, unit) - return x.truncate(unit_mapped) + # Floor is the same as truncate. + return self._round(x, "FLOOR", unit) + + def _round(self, x: IbisTemporalExpr, rounding: str, unit: str) -> IbisValueExpr: + """Redirect through the real round_temporal/round_calendar + implementation (item 74). `unit` is already normalize_unit()'d and + validate_ma_option()'d by the builder before it reaches here.""" + from mountainash.expressions.core.expression_api.api_builders.extensions_mountainash._ma_option_domains import parse_ma_unit + + multiple, canonical_unit, family = parse_ma_unit(unit) + if family == "calendar": + return self.round_calendar( + x, rounding=rounding, unit=canonical_unit, multiple=multiple + ) + return self.round_temporal( + x, rounding=rounding, unit=canonical_unit, multiple=multiple + ) # ========================================================================= # Timezone Methods diff --git a/src/mountainash/expressions/backends/expression_systems/ibis/substrait/expsys_ib_scalar_datetime.py b/src/mountainash/expressions/backends/expression_systems/ibis/substrait/expsys_ib_scalar_datetime.py index b82a328a..1f291cbe 100644 --- a/src/mountainash/expressions/backends/expression_systems/ibis/substrait/expsys_ib_scalar_datetime.py +++ b/src/mountainash/expressions/backends/expression_systems/ibis/substrait/expsys_ib_scalar_datetime.py @@ -10,7 +10,7 @@ CALENDAR_COMPONENTS, DatetimeComponent, ) -from typing import Any, TYPE_CHECKING, Optional +from typing import Any, TYPE_CHECKING import ibis diff --git a/src/mountainash/expressions/backends/expression_systems/narwhals/extensions_mountainash/expsys_nw_ext_ma_scalar_datetime.py b/src/mountainash/expressions/backends/expression_systems/narwhals/extensions_mountainash/expsys_nw_ext_ma_scalar_datetime.py index cb03b425..e8a2664d 100644 --- a/src/mountainash/expressions/backends/expression_systems/narwhals/extensions_mountainash/expsys_nw_ext_ma_scalar_datetime.py +++ b/src/mountainash/expressions/backends/expression_systems/narwhals/extensions_mountainash/expsys_nw_ext_ma_scalar_datetime.py @@ -424,7 +424,7 @@ def truncate( Returns: Truncated datetime. """ - return x.dt.truncate(unit) + return self._round(x, "FLOOR", unit) def round_dt( self, @@ -440,12 +440,8 @@ def round_dt( Returns: Rounded datetime. - - Note: - Narwhals may not have round. Falls back to truncate. """ - # Narwhals doesn't have round - fallback to truncate - return x.dt.truncate(unit) + return self._round(x, "ROUND_TIE_UP", unit) def ceil_dt( self, @@ -461,12 +457,8 @@ def ceil_dt( Returns: Ceiling datetime. - - Note: - Narwhals doesn't have ceil. Falls back to truncate. """ - # Narwhals doesn't have ceil - fallback to truncate - return x.dt.truncate(unit) + return self._round(x, "CEIL", unit) def floor_dt( self, @@ -483,7 +475,23 @@ def floor_dt( Returns: Floor datetime. """ - return x.dt.truncate(unit) + # Floor is the same as truncate. + return self._round(x, "FLOOR", unit) + + def _round(self, x: NarwhalsExpr, rounding: str, unit: str) -> NarwhalsExpr: + """Redirect through the real round_temporal/round_calendar + implementation (item 74). `unit` is already normalize_unit()'d and + validate_ma_option()'d by the builder before it reaches here.""" + from mountainash.expressions.core.expression_api.api_builders.extensions_mountainash._ma_option_domains import parse_ma_unit + + multiple, canonical_unit, family = parse_ma_unit(unit) + if family == "calendar": + return self.round_calendar( + x, rounding=rounding, unit=canonical_unit, multiple=multiple + ) + return self.round_temporal( + x, rounding=rounding, unit=canonical_unit, multiple=multiple + ) # ========================================================================= # Timezone Methods diff --git a/src/mountainash/expressions/backends/expression_systems/narwhals/substrait/expsys_nw_scalar_datetime.py b/src/mountainash/expressions/backends/expression_systems/narwhals/substrait/expsys_nw_scalar_datetime.py index d875fb25..f7a38ef6 100644 --- a/src/mountainash/expressions/backends/expression_systems/narwhals/substrait/expsys_nw_scalar_datetime.py +++ b/src/mountainash/expressions/backends/expression_systems/narwhals/substrait/expsys_nw_scalar_datetime.py @@ -10,7 +10,7 @@ CALENDAR_COMPONENTS, DatetimeComponent, ) -from typing import Any, TYPE_CHECKING, Optional +from typing import Any, TYPE_CHECKING import narwhals as nw diff --git a/src/mountainash/expressions/backends/expression_systems/polars/extensions_mountainash/expsys_pl_ext_ma_scalar_datetime.py b/src/mountainash/expressions/backends/expression_systems/polars/extensions_mountainash/expsys_pl_ext_ma_scalar_datetime.py index 20ca38b8..4a4cd768 100644 --- a/src/mountainash/expressions/backends/expression_systems/polars/extensions_mountainash/expsys_pl_ext_ma_scalar_datetime.py +++ b/src/mountainash/expressions/backends/expression_systems/polars/extensions_mountainash/expsys_pl_ext_ma_scalar_datetime.py @@ -409,7 +409,7 @@ def truncate( Returns: Truncated datetime. """ - return x.dt.truncate(unit) + return self._round(x, "FLOOR", unit) def round_dt( self, @@ -426,7 +426,7 @@ def round_dt( Returns: Rounded datetime. """ - return x.dt.round(unit) + return self._round(x, "ROUND_TIE_UP", unit) def ceil_dt( self, @@ -443,10 +443,7 @@ def ceil_dt( Returns: Ceiling datetime. """ - # Polars doesn't have ceil for datetime, use truncate + add - truncated = x.dt.truncate(unit) - # If truncated != original, add one unit - return pl.when(truncated == x).then(x).otherwise(truncated.dt.offset_by(unit)) + return self._round(x, "CEIL", unit) def floor_dt( self, @@ -463,8 +460,23 @@ def floor_dt( Returns: Floor datetime. """ - # Floor is the same as truncate - return x.dt.truncate(unit) + # Floor is the same as truncate. + return self._round(x, "FLOOR", unit) + + def _round(self, x: PolarsExpr, rounding: str, unit: str) -> PolarsExpr: + """Redirect through the real round_temporal/round_calendar + implementation (item 74). `unit` is already normalize_unit()'d and + validate_ma_option()'d by the builder before it reaches here.""" + from mountainash.expressions.core.expression_api.api_builders.extensions_mountainash._ma_option_domains import parse_ma_unit + + multiple, canonical_unit, family = parse_ma_unit(unit) + if family == "calendar": + return self.round_calendar( + x, rounding=rounding, unit=canonical_unit, multiple=multiple + ) + return self.round_temporal( + x, rounding=rounding, unit=canonical_unit, multiple=multiple + ) # ========================================================================= # Timezone Methods diff --git a/src/mountainash/expressions/core/expression_api/api_builders/extensions_mountainash/_ma_option_domains.py b/src/mountainash/expressions/core/expression_api/api_builders/extensions_mountainash/_ma_option_domains.py index 82af0e26..f964b36d 100644 --- a/src/mountainash/expressions/core/expression_api/api_builders/extensions_mountainash/_ma_option_domains.py +++ b/src/mountainash/expressions/core/expression_api/api_builders/extensions_mountainash/_ma_option_domains.py @@ -99,3 +99,41 @@ def validate_open_value(value_class: Any, param: str, value: Any, op: str) -> An ) return value + + +import re as _re # noqa: E402 -- module-local, avoids widening the top-level import block + +# Canonical Substrait unit each MA duration suffix maps to. "q" (quarter) +# maps to MONTH -- its multiplier is folded to a multiple-of-3 by +# parse_ma_unit below (real Substrait has no QUARTER unit; item 74 spec §3.2). +_MA_SUFFIX_TO_CANONICAL = { + "y": "YEAR", "mo": "MONTH", "q": "MONTH", "w": "WEEK", "d": "DAY", + "h": "HOUR", "m": "MINUTE", "s": "SECOND", "ms": "MILLISECOND", "us": "MICROSECOND", +} +_MA_CALENDAR_UNITS = frozenset({"YEAR", "MONTH", "WEEK"}) +_MA_UNIT_RE = _re.compile(r"^(\d+)([a-z]+)$") + + +def parse_ma_unit(value: str) -> tuple[int, str, str]: + """Parse an already-validated, alias-normalized MA duration string (e.g. + "2d" -> (2, "DAY", "temporal") or "1mo" -> (1, "MONTH", "calendar")) for + the round_temporal/round_calendar redirect (item 74). `value` must + already be normalize_unit()-normalized (friendly words resolved to their + duration form) and validate_ma_option()-validated (multiplier + suffix + known-legal) -- this function does not itself validate. + + "1q" (quarter) folds to (3, "MONTH", "calendar") -- multiplier composes + for multi-digit quarter input too, e.g. "2q" -> (6, "MONTH", "calendar"). + """ + match = _MA_UNIT_RE.match(value) + if match is None: + raise InvalidOptionValueError(f"cannot parse MA duration unit {value!r}") + multiplier = int(match.group(1)) + suffix = match.group(2) + canonical = _MA_SUFFIX_TO_CANONICAL.get(suffix) + if canonical is None: + raise InvalidOptionValueError(f"unknown MA duration suffix {suffix!r} in {value!r}") + if suffix == "q": + multiplier *= 3 + family = "calendar" if canonical in _MA_CALENDAR_UNITS else "temporal" + return multiplier, canonical, family diff --git a/tests/core/_smoke_helpers.py b/tests/core/_smoke_helpers.py index d93dd76a..99d77bf7 100644 --- a/tests/core/_smoke_helpers.py +++ b/tests/core/_smoke_helpers.py @@ -45,6 +45,14 @@ def _init_smoke_overrides() -> dict[Enum, tuple[list[Any], dict[str, Any]]]: FKEY_SUBSTRAIT_SCALAR_DATETIME.ASSUME_TIMEZONE: ([ma.col("a"), "UTC"], {}), FKEY_MOUNTAINASH_SCALAR_DATETIME.TO_TIMEZONE: ([ma.col("a"), "UTC"], {}), FKEY_SUBSTRAIT_SCALAR_DATETIME.LOCAL_TIMESTAMP: ([ma.col("a"), "UTC"], {}), + FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_TEMPORAL: ( + [ma.col("a")], + {"rounding": "FLOOR", "unit": "DAY"}, + ), + FKEY_SUBSTRAIT_SCALAR_DATETIME.ROUND_CALENDAR: ( + [ma.col("a")], + {"rounding": "FLOOR", "unit": "MONTH"}, + ), } diff --git a/tests/core/test_capability_protocol_guard.py b/tests/core/test_capability_protocol_guard.py index 3491ffd5..f9a967f6 100644 --- a/tests/core/test_capability_protocol_guard.py +++ b/tests/core/test_capability_protocol_guard.py @@ -30,6 +30,7 @@ "mountainash.expressions.backends.capabilities.arithmetic", "mountainash.expressions.backends.capabilities.datetime.extract", "mountainash.expressions.backends.capabilities.datetime.options", + "mountainash.expressions.backends.capabilities.datetime.rounding", "mountainash.expressions.backends.capabilities.datetime.strptime", "mountainash.expressions.backends.capabilities.datetime.value_classes_ma", "mountainash.expressions.backends.capabilities.datetime.value_classes_substrait", @@ -125,7 +126,7 @@ def _domain_predicate(leaf: str): domains = { "string": Domain.STRING, "arithmetic": Domain.ARITHMETIC, "options": Domain.DATETIME, "strptime": Domain.DATETIME, - "extract": Domain.DATETIME, + "extract": Domain.DATETIME, "rounding": Domain.DATETIME, } want = domains[leaf] return lambda f: ( diff --git a/tests/expressions/argument_types/option_disposition.py b/tests/expressions/argument_types/option_disposition.py index 820e2427..f86fa293 100644 --- a/tests/expressions/argument_types/option_disposition.py +++ b/tests/expressions/argument_types/option_disposition.py @@ -277,6 +277,12 @@ class InvalidOptionRejection(NamedTuple): ("round_dt", "unit"): ("datetime",), ("ceil_dt", "unit"): ("datetime",), ("floor_dt", "unit"): ("datetime",), + # Substrait round_temporal/round_calendar (item 74) — shared 9-unit + # closed domain lives in OPTION_DOMAINS. + ("round_temporal", "unit"): ("datetime",), + ("round_temporal", "rounding"): ("float64",), + ("round_calendar", "unit"): ("datetime",), + ("round_calendar", "rounding"): ("float64",), # Datetime open-value options. ("assume_timezone", "timezone"): ("datetime",), ("to_timezone", "timezone"): ("datetime",), diff --git a/tests/expressions/argument_types/test_arg_types_datetime.py b/tests/expressions/argument_types/test_arg_types_datetime.py index 6908ab9d..6febf8d2 100644 --- a/tests/expressions/argument_types/test_arg_types_datetime.py +++ b/tests/expressions/argument_types/test_arg_types_datetime.py @@ -583,31 +583,24 @@ def _unit_reference_expr(op: str, build_value: str): return _unit_expr(op, _REFERENCE_VALUE_BY_BUILD[build_value]) -# (op, value) pairs HELD OUT of the ibis-duckdb honor set; declared on ibis. -# Friendly aliases inherit the same disposition as their canonical duration -# form ("quarter" -> "1q", "week" -> "1w"), per the api-builder's friendly -# normalization at validate_ma_option. -_IBIS_DUCKDB_DECLARED_DURATION = { - "truncate": {"1q", "2d", "3h"}, - "floor_dt": {"1q", "2d", "3h"}, - "round_dt": {"1y", "1mo", "1d", "1h", "1m", "1s", "1ms", "1us", "1w", "1q", "2d", "3h"}, - "ceil_dt": {"1y", "1mo", "1d", "1h", "1m", "1s", "1ms", "1us", "1w", "1q", "2d", "3h"}, -} +# ibis-duckdb (this generic "ibis" test fixture) now honors EVERY value — +# round_dt/ceil_dt redirect through the real round_temporal/round_calendar +# implementation instead of a silent-wrong truncate fallback (item 74). +# narwhals still declares only "1w" (dt.truncate rejects the week duration +# on both dialects) — now uniformly across all 4 ops, not just +# truncate/floor_dt (round_dt/ceil_dt redirect through the same +# truncate-based body and inherit the identical gap). _NARWHALS_DECLARED_DURATION = { "truncate": {"1w"}, "floor_dt": {"1w"}, - "round_dt": {"1y", "1mo", "1d", "1h", "1m", "1s", "1ms", "1us", "1w", "1q", "2d", "3h"}, - "ceil_dt": {"1y", "1mo", "1d", "1h", "1m", "1s", "1ms", "1us", "1w", "1q", "2d", "3h"}, + "round_dt": {"1w"}, + "ceil_dt": {"1w"}, } _FRIENDLY_TO_DURATION = { "year": "1y", "quarter": "1q", "month": "1mo", "week": "1w", "day": "1d", "hour": "1h", "minute": "1m", "second": "1s", "millisecond": "1ms", "microsecond": "1us", } -_IBIS_DUCKDB_DECLARED = { - op: declared | {friendly for friendly, dur in _FRIENDLY_TO_DURATION.items() if dur in declared} - for op, declared in _IBIS_DUCKDB_DECLARED_DURATION.items() -} _NARWHALS_DECLARED = { op: declared | {friendly for friendly, dur in _FRIENDLY_TO_DURATION.items() if dur in declared} for op, declared in _NARWHALS_DECLARED_DURATION.items() @@ -616,38 +609,21 @@ def _unit_reference_expr(op: str, build_value: str): def _unit_disposition(op: str, value: str, backend: str) -> str: """Mirror the verified matrix: honored or declared_unsupported per cell.""" - if backend == "polars": - return "honored" # polars honors ALL values; the matrix column - if backend == "ibis": - return "declared_unsupported" if value in _IBIS_DUCKDB_DECLARED[op] else "honored" + if backend in ("polars", "ibis"): + return "honored" # both fully honor every value now (item 74). # narwhals-polars and narwhals-pandas share the same per-dialect fact sets. return "declared_unsupported" if value in _NARWHALS_DECLARED[op] else "honored" def _unit_backing_mode(op: str, value: str, backend: str) -> str: - disp = _unit_disposition(op, value, backend) - if disp != "declared_unsupported": - return "absence" - if value in {"2d", "3h"}: - return "class" - return "exact-fallback" + return "absence" if _unit_disposition(op, value, backend) != "declared_unsupported" else "exact-fallback" def _unit_reason(op: str, value: str, backend: str) -> str: if _unit_disposition(op, value, backend) == "honored": return "native backend honors the unit on this op" - if backend == "ibis": - if value in {"2d", "3h"}: - return "ibis TimestampTruncate rejects Polars-style multiplier duration units (e.g. '2d', '3h', '12mo'); only single bare units are accepted" - if op in {"round_dt", "ceil_dt"}: - return "ibis has no native datetime round/ceil; silently falling back to truncate would return a wrong value" - return "ibis TimestampTruncate rejects the quarter unit '1q' (and its friendly alias 'quarter')" - # narwhals - if value in {"2d", "3h"}: - return "narwhals has no native datetime round/ceil; a multiplier value silently falls back to truncate and returns a wrong (down-rounded) result" - if op in {"round_dt", "ceil_dt"}: - return "narwhals has no native datetime round/ceil; silently falling back to truncate would return a wrong value" - return "narwhals truncate rejects the week unit '1w' (and its friendly alias 'week')" + # narwhals -- the only remaining declared cell (week). + return "narwhals dt.truncate rejects the week unit '1w' (and its friendly alias 'week') on both dialects" def _unit_probe(op: str, value: str, backend: str) -> OptionSpec: @@ -663,52 +639,24 @@ def _unit_probe(op: str, value: str, backend: str) -> OptionSpec: _DATETIME_UNIT_DATA, expected_discriminates=True, ) - # declared_unsupported - if backend == "ibis": - return OptionSpec( - _UNIT_OP_FKEYS[op], - "unit", - value, - "datetime", - lambda v=value: _unit_expr(op, v), - lambda v=value: _unit_reference_expr(op, v), - _DATETIME_UNIT_DATA, - expected_discriminates=True, - ) - # narwhals - canonical = _FRIENDLY_TO_DURATION.get(value, value) - if canonical == "1w": - return OptionSpec( - _UNIT_OP_FKEYS[op], - "unit", - value, - "datetime", - lambda v=value: _unit_expr(op, v), - lambda v=value: _unit_reference_expr(op, v), - _DATETIME_UNIT_DATA, - expected_discriminates=True, - ) + # declared_unsupported (narwhals, week only) return OptionSpec( _UNIT_OP_FKEYS[op], "unit", value, "datetime", lambda v=value: _unit_expr(op, v), - lambda v=value: _unit_expr(op, v), # same value → always equal + lambda v=value: _unit_reference_expr(op, v), _DATETIME_UNIT_DATA, - expected_discriminates=True, # mismatch: probe raises sentinel + expected_discriminates=True, ) def _unit_native_failure(op: str, value: str, backend: str): - if backend == "ibis": - from ibis.common.annotations import SignatureValidationError - return SignatureValidationError - # narwhals - canonical = _FRIENDLY_TO_DURATION.get(value, value) - if canonical == "1w": - return ValueError - return OptionProbeDidNotDiscriminateError + # Only reachable for narwhals' declared "1w" cell -- the MA wrapper + # redirects through round_calendar, whose own defence-in-depth raise + # (BackendCapabilityError) pre-empts narwhals' underlying raw ValueError. + return BackendCapabilityError # Per-op, per-unit HONORED result (verified by the controller Step-0 probe). @@ -823,13 +771,6 @@ def test_datetime_unit_option_honored_or_declared( for value in _ALL_UNIT_VALUES ) -OPTION_FAMILY_DEFAULT_FACT_KEYS.update( - (_UNIT_OP_FKEYS[op], "unit", value, CONST_BACKEND.IBIS, None) - for op in sorted(_UNIT_OP_FKEYS) - for value in _ALL_UNIT_VALUES - if value in _IBIS_DUCKDB_DECLARED[op] and value not in {"2d", "3h"} -) - def _datetime_unit_invalid_expr(op: str, value: str): return _unit_expr(op, value) diff --git a/tests/expressions/argument_types/test_coverage_guard.py b/tests/expressions/argument_types/test_coverage_guard.py index 91e3682f..bc7b05cc 100644 --- a/tests/expressions/argument_types/test_coverage_guard.py +++ b/tests/expressions/argument_types/test_coverage_guard.py @@ -94,11 +94,13 @@ def _is_dst_returns_constant_false() -> bool: return all(v is False for v in out["d"].to_list()) -def _round_temporal_calendar_backends_are_broken() -> bool: - """round_temporal/round_calendar backend bodies are still stub placeholders - (item 74, Tasks 4-6). Build a column and confirm the call still fails on - Polars (the reference backend). Returns False once real per-backend - bodies are wired (a correct compile no longer raises).""" +def _round_temporal_calendar_still_works() -> bool: + """round_temporal/round_calendar are real, cross-backend-tested + implementations (item 74) covered by a dedicated plain pytest file + rather than this module's disposition matrix. Build a column and + confirm the call still succeeds on Polars (the reference backend). + Returns False if this op ever regresses back to broken -- the parked + reason claiming coverage exists elsewhere would then need re-examining.""" from datetime import datetime import polars as pl @@ -110,9 +112,9 @@ def _round_temporal_calendar_backends_are_broken() -> bool: ma.relation(df).with_columns( ma.col("ts").dt.round_temporal(rounding="FLOOR", unit="DAY").name.alias("d") ).to_polars() - return False - except Exception: return True + except Exception: + return False def _round_origin_always_rejected() -> bool: @@ -860,64 +862,76 @@ def test_collect_option_param_taxonomy_rejects_conflicting_duplicates(monkeypatc } } ) -# round_temporal/round_calendar (item 74): rounding/unit/multiple ARE wired -# real options as of Task 2-3 (closed OPTION_DOMAINS, API-builder validated), -# but polars/ibis/narwhals backend bodies are still stub placeholders pending -# Tasks 4-6 -- no cross-backend disposition exists yet to move these into -# TESTED_OPTION_PARAMS. origin is permanently excluded from v1 (real Substrait -# types it as an arguments-channel value, not a string option; any non-None -# value always raises InvalidOptionValueError at build time regardless of -# backend) -- it has no legal value to ever test. +# round_temporal/round_calendar (item 74): rounding/unit/multiple ARE real, +# fully implemented, and cross-backend tested -- but via a dedicated plain +# pytest file (tests/expressions/cross_backend/test_datetime_rounding.py), +# not via this module's OPTION_DISPOSITIONS/param_taxonomy machinery (that +# would require building a full per-value disposition matrix mirroring +# truncate/round_dt/ceil_dt/floor_dt's _unit_* helpers above, out of scope +# for this PR). origin is permanently excluded from v1 (real Substrait +# types it as an arguments-channel value, not a string option; any +# non-None value always raises InvalidOptionValueError at build time +# regardless of backend) -- it has no legal value to ever test. _KNOWN_UNTESTED_OPTION_PARAMS.update( { ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "multiple"): KnownGap( gap_kind=GapKind.UNTESTED_OPTION, reason=( - "wired real option (Task 2-3); backend bodies are still stub " - "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + "tested via a dedicated cross-backend test file, not this " + "module's disposition matrix — see backlog: " + "round-temporal-calendar-real-implementation" ), since="2026-08-16", ), ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "rounding"): KnownGap( gap_kind=GapKind.UNTESTED_OPTION, reason=( - "wired real option (Task 2-3); backend bodies are still stub " - "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + "tested via a dedicated cross-backend test file, not this " + "module's disposition matrix — see backlog: " + "round-temporal-calendar-real-implementation" ), since="2026-08-16", ), ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "unit"): KnownGap( gap_kind=GapKind.UNTESTED_OPTION, reason=( - "wired real option (Task 2-3); backend bodies are still stub " - "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + "tested via a dedicated cross-backend test file, not this " + "module's disposition matrix — see backlog: " + "round-temporal-calendar-real-implementation" ), since="2026-08-16", ), ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "multiple"): KnownGap( gap_kind=GapKind.UNTESTED_OPTION, reason=( - "wired real option (Task 2-3); backend bodies are still stub " - "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + "tested via a dedicated cross-backend test file, not this " + "module's disposition matrix — see backlog: " + "round-temporal-calendar-real-implementation" ), since="2026-08-16", ), ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "rounding"): KnownGap( gap_kind=GapKind.UNTESTED_OPTION, reason=( - "wired real option (Task 2-3); backend bodies are still stub " - "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + "tested via a dedicated cross-backend test file, not this " + "module's disposition matrix — see backlog: " + "round-temporal-calendar-real-implementation" ), since="2026-08-16", ), ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_temporal", "unit"): KnownGap( gap_kind=GapKind.UNTESTED_OPTION, reason=( - "wired real option (Task 2-3); backend bodies are still stub " - "placeholders (Tasks 4-6) — see backlog: round-temporal-calendar-real-implementation" + "tested via a dedicated cross-backend test file, not this " + "module's disposition matrix — see backlog: " + "round-temporal-calendar-real-implementation" ), since="2026-08-16", ), + } +) +_KNOWN_UNTESTED_OPTION_PARAMS.update( + { ("SubstraitScalarDatetimeExpressionSystemProtocol", "round_calendar", "origin"): KnownGap( gap_kind=GapKind.UNTESTED_OPTION, reason=( @@ -1843,9 +1857,9 @@ def test_untested_option_param_custom_reasons_still_hold(): elif "placeholder stub" in r: if not _is_dst_returns_constant_false(): reason_false.append(((proto, op, param), "is_dst no longer returns constant False")) - elif "backend bodies are still stub placeholders" in r: - if not _round_temporal_calendar_backends_are_broken(): - reason_false.append(((proto, op, param), "round_temporal/round_calendar backend bodies no longer raise (implemented)")) + elif "tested via a dedicated cross-backend test file" in r: + if not _round_temporal_calendar_still_works(): + reason_false.append(((proto, op, param), "round_temporal/round_calendar no longer works — its dedicated test file coverage claim needs re-examining")) elif "origin is permanently excluded from v1" in r: if not _round_origin_always_rejected(): reason_false.append(((proto, op, param), "round_temporal/round_calendar origin no longer always raises")) diff --git a/tests/expressions/argument_types/test_option_fact_integrity.py b/tests/expressions/argument_types/test_option_fact_integrity.py index 9f93d191..44efa6fa 100644 --- a/tests/expressions/argument_types/test_option_fact_integrity.py +++ b/tests/expressions/argument_types/test_option_fact_integrity.py @@ -51,7 +51,9 @@ ) from mountainash.core.constants import CONST_BACKEND from mountainash.expressions.core.expression_system.function_keys.enums import ( + FKEY_MOUNTAINASH_SCALAR_DATETIME as FK_MA_DT, FKEY_SUBSTRAIT_SCALAR_ARITHMETIC as FK_ARITH, + FKEY_SUBSTRAIT_SCALAR_DATETIME as FK_SUB_DT, FKEY_SUBSTRAIT_SCALAR_STRING as FK_STR, ) from mountainash.expressions.core.expression_system.function_mapping.registry import ( @@ -234,6 +236,40 @@ def test_no_op_level_fact_is_left_unbacked() -> None: } | { (fkey, "case_sensitivity", "CASE_INSENSITIVE", CONST_BACKEND.IBIS, "ibis-sqlite") for fkey in (FK_STR.CONTAINS, FK_STR.STARTS_WITH, FK_STR.ENDS_WITH) +} | { + # item 74: truncate/round_dt/ceil_dt/floor_dt redirect through the real + # round_temporal/round_calendar implementation, which has genuine + # ibis-sqlite (sub-day units + quarter) and ibis-polars (calendar-unit + # round/ceil) gaps -- verified directly by + # TestDatetimeUnitIbisSqliteGate / TestDatetimeUnitIbisPolarsGate in + # test_arg_types_datetime.py (this matrix's "ibis" fixture is hardcoded + # to ibis-duckdb, which honors every value). + (fkey, "unit", value, CONST_BACKEND.IBIS, "ibis-sqlite") + for fkey in (FK_MA_DT.TRUNCATE, FK_MA_DT.ROUND, FK_MA_DT.CEIL, FK_MA_DT.FLOOR) + for value in ( + "1h", "1m", "1s", "1ms", "1us", "1q", + "hour", "minute", "second", "millisecond", "microsecond", "quarter", + ) +} | { + (fkey, "unit", value, CONST_BACKEND.IBIS, "ibis-polars") + for fkey in (FK_MA_DT.ROUND, FK_MA_DT.CEIL) + for value in ("1y", "1mo", "1q", "year", "month", "quarter") +} | { + # item 74: capabilities/datetime/rounding.py's direct Substrait + # round_temporal/round_calendar facts for the same two dialect gaps -- + # verified directly by test_datetime_rounding.py's + # TestRoundTemporalSqliteSubDayUnsupported / + # TestRoundCalendarPolarsMonthYearUnsupported / + # TestRoundCalendarSqliteMultipleUnsupported. + (fkey, "unit", value, CONST_BACKEND.IBIS, "ibis-sqlite") + for fkey in (FK_SUB_DT.ROUND_TEMPORAL, FK_SUB_DT.ROUND_CALENDAR) + for value in ("HOUR", "MINUTE", "SECOND", "MILLISECOND", "MICROSECOND") +} | { + (FK_SUB_DT.ROUND_TEMPORAL, "multiple", "2", CONST_BACKEND.IBIS, "ibis-sqlite"), + (FK_SUB_DT.ROUND_CALENDAR, "multiple", "3", CONST_BACKEND.IBIS, "ibis-sqlite"), +} | { + (FK_SUB_DT.ROUND_CALENDAR, "unit", value, CONST_BACKEND.IBIS, "ibis-polars") + for value in ("MONTH", "YEAR") } diff --git a/tests/expressions/cross_backend/test_datetime_unit_dispatch.py b/tests/expressions/cross_backend/test_datetime_unit_dispatch.py index 208f1199..acefe4c9 100644 --- a/tests/expressions/cross_backend/test_datetime_unit_dispatch.py +++ b/tests/expressions/cross_backend/test_datetime_unit_dispatch.py @@ -54,19 +54,13 @@ "narwhals-pandas", ) -# (backend, op) → reason. round/ceil have no native datetime impl on ibis or narwhals -# (the silent truncate-fallback is an anti-pattern); Task 3 declares them UNSUPPORTED so -# the visitor raises a clean BackendCapabilityError. ibis floor was fixed in Task 3 -# (floor == truncate; applies the unit-mapping) and is NO LONGER divergent — its removal -# from this map was forced by the strict xfail flipping to XPASS, exactly as intended. -_KNOWN_FALLBACK_DIVERGENCES: dict[tuple[str, str], str] = { - ("ibis-duckdb", "round"): "ibis has no native datetime round; declared UNSUPPORTED -> BackendCapabilityError (Task 3)", - ("ibis-duckdb", "ceil"): "ibis has no native datetime ceil; declared UNSUPPORTED -> BackendCapabilityError (Task 3)", - ("narwhals-polars", "round"): "narwhals has no native datetime round; declared UNSUPPORTED -> BackendCapabilityError (Task 3)", - ("narwhals-polars", "ceil"): "narwhals has no native datetime ceil; declared UNSUPPORTED -> BackendCapabilityError (Task 3)", - ("narwhals-pandas", "round"): "narwhals has no native datetime round; declared UNSUPPORTED -> BackendCapabilityError (Task 3)", - ("narwhals-pandas", "ceil"): "narwhals has no native datetime ceil; declared UNSUPPORTED -> BackendCapabilityError (Task 3)", -} +# (backend, op) → reason. Historically ibis/narwhals had no native datetime +# round/ceil (silent truncate-fallback anti-pattern); item 74's round_temporal/ +# round_calendar redirect gave every backend a real implementation, so this +# map is now empty. ibis floor was fixed first (Task 3 of the earlier plan); +# round/ceil followed via item 74 — each removal was forced by the strict +# xfail flipping to XPASS, exactly as intended. +_KNOWN_FALLBACK_DIVERGENCES: dict[tuple[str, str], str] = {} @pytest.mark.cross_backend diff --git a/tests/expressions/cross_backend/test_datetime_value_class_dispatch.py b/tests/expressions/cross_backend/test_datetime_value_class_dispatch.py index 34f1b3b7..e4911ae9 100644 --- a/tests/expressions/cross_backend/test_datetime_value_class_dispatch.py +++ b/tests/expressions/cross_backend/test_datetime_value_class_dispatch.py @@ -2,22 +2,13 @@ Encodes the controller Task-5 semantic probe matrix (`.superpowers/sdd/vc-probe-matrix.md`) as an explicit regression lock — NOT a -re-probe. DURATION_MULTIPLIER (integer unit multipliers >= 2, e.g. "2d"/"3h"/ -"12mo") and IANA_TIMEZONE (`assume_timezone`) value-class facts must gate the +re-probe. IANA_TIMEZONE (`assume_timezone`) value-class facts must gate the declared (op, backend) cells with a clean ``BackendCapabilityError``, while honored cells compile and evaluate. Probe matrix (every value in each cell AGREES — no partial/disagree cells, so there are no xfails here): - DURATION_MULTIPLIER - - ibis (ibis-duckdb): RAISED on truncate / round / ceil / floor (all four) - — TimestampTruncate rejects the Polars-style duration form. - - narwhals-{polars,pandas}: SILENTLY-WRONG on round / ceil (no native - datetime round/ceil -> truncate fallback) — DECLARED; HONORED on - truncate / floor. - - polars: honored on all four. - IANA_TIMEZONE :: assume_timezone - ibis + both narwhals dialects: SILENTLY-WRONG (drop the tz, return a naive timestamp) — DECLARED. @@ -27,6 +18,14 @@ option is non-functional, so it carries NO value-class fact and stays parked (see backlog: is_dst-placeholder-implementation). `offset_by` and `strftime` are honored on every fixture — no facts. + +DURATION_MULTIPLIER (integer unit multipliers >= 2, e.g. "2d"/"3h"/"12mo") +facts and this file's former `test_multiplier_gate` regression were RETIRED +by item 74: truncate/round_dt/ceil_dt/floor_dt now redirect through the real +round_temporal/round_calendar implementation instead of a silent-wrong +truncate fallback, so every fixture honors every multiplier value (see +`src/mountainash/expressions/backends/capabilities/datetime/value_classes_ma.py` +module docstring for the re-probed disposition table). """ from __future__ import annotations @@ -46,7 +45,6 @@ from mountainash.core.constants import CONST_BACKEND from mountainash.core.types import BackendCapabilityError from mountainash.expressions.core.expression_system.function_keys.enums import ( - FKEY_MOUNTAINASH_SCALAR_DATETIME as M, FKEY_SUBSTRAIT_SCALAR_DATETIME as S, ) @@ -59,22 +57,9 @@ # (fkey, param, backend, dialect, value_class) tuples that MUST resolve to an # UNSUPPORTED value-class fact. ibis carries a family-default (dialect=None) AND # an ibis-duckdb fact; narwhals is per-dialect only. -_MULT = ValueClass.DURATION_MULTIPLIER _TZ = ValueClass.IANA_TIMEZONE EXPECTED_DECLARED_CLASS_FACTS = [ - # ibis multiplier: all four rounding ops, family default + duckdb - *[ - (fkey, "unit", CONST_BACKEND.IBIS, dialect, _MULT) - for fkey in (M.TRUNCATE, M.ROUND, M.CEIL, M.FLOOR) - for dialect in (None, "ibis-duckdb") - ], - # narwhals multiplier: round/ceil only, per-dialect - *[ - (fkey, "unit", CONST_BACKEND.NARWHALS, dialect, _MULT) - for fkey in (M.ROUND, M.CEIL) - for dialect in ("narwhals-polars", "narwhals-pandas") - ], # assume_timezone IANA: ibis family default + duckdb; narwhals per-dialect *[ (S.ASSUME_TIMEZONE, "timezone", CONST_BACKEND.IBIS, dialect, _TZ) @@ -87,7 +72,6 @@ ] _REPRESENTATIVE_VALUE = { - _MULT: "2d", _TZ: "Australia/Sydney", } @@ -105,37 +89,6 @@ def test_declared_class_cells_have_facts(): # --- Cross-backend production regression ---------------------------------- -# (backend, public-op) cells the multiplier class DECLARES unsupported. -_MULT_DECLARED = { - ("ibis-duckdb", "truncate"), - ("ibis-duckdb", "round"), - ("ibis-duckdb", "ceil"), - ("ibis-duckdb", "floor"), - ("narwhals-polars", "round"), - ("narwhals-polars", "ceil"), - ("narwhals-pandas", "round"), - ("narwhals-pandas", "ceil"), -} - - -@pytest.mark.cross_backend -@pytest.mark.parametrize("backend_name", ALL_BACKENDS) -@pytest.mark.parametrize("op", ["truncate", "round", "ceil", "floor"]) -@pytest.mark.parametrize("value", ["2d", "3h"]) -def test_multiplier_gate(backend_factory, collect_expr, backend_name, op, value): - """A multiplier on a declared (backend, op) raises BackendCapabilityError at - compile (NOT a silent truncate / raw backend error); an honored cell - evaluates to a datetime.""" - df = backend_factory.create({"ts": [_TS]}, backend_name) - expr = getattr(ma.col("ts").dt, op)(value) - if (backend_name, op) in _MULT_DECLARED: - with pytest.raises(BackendCapabilityError): - collect_expr(df, expr, alias="r") - else: - got = collect_expr(df, expr, alias="r") - assert isinstance(got[0], datetime) - - @pytest.mark.cross_backend @pytest.mark.parametrize("backend_name", ALL_BACKENDS) @pytest.mark.parametrize("tz", ["UTC", "Australia/Sydney"]) From 690529f9d99446a98acc68c787d532da77e57528 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 17 Aug 2026 10:34:19 +1000 Subject: [PATCH 8/8] docs: regenerate expression-coverage after merging develop --- docs/reference/expression-coverage-scoped.md | 9 +- docs/reference/expression-coverage.json | 89 +++++++++++++++++--- docs/reference/expression-coverage.md | 12 +-- 3 files changed, 91 insertions(+), 19 deletions(-) diff --git a/docs/reference/expression-coverage-scoped.md b/docs/reference/expression-coverage-scoped.md index 81f088c6..f0d97929 100644 --- a/docs/reference/expression-coverage-scoped.md +++ b/docs/reference/expression-coverage-scoped.md @@ -5,7 +5,7 @@ Scoped deviations — dialect, parameter, option, value-class; function-level coverage and matrices live in [`expression-coverage.md`](expression-coverage.md). -Declarations: 43 · Facts: 1460 · Registered operations: 326 · Implementation records: 978 +Declarations: 43 · Facts: 1462 · Registered operations: 326 · Implementation records: 978 Legend — scoped deviations: @@ -158,6 +158,13 @@ Legend — scoped deviations: | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | ibis-sqlite | unit | 1h, 1m, 1ms, 1q, 1s, 1us, hour, microsecond, millisecond, minute, quarter, second | — | unsupported | gate | build | — | ibis-sqlite has no TimestampTruncate support for units finer than DAY, and no TimestampBucket compilation rule (blocks multiple>1 bucketing, which quarter needs); verified 2026-08-16, ibis 12.0.0 | — | — | 2026-08-16 | — | — | +### `IS_DST` × ibis (FKEY_MOUNTAINASH_SCALAR_DATETIME) + +| Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| * | timezone | — | iana_timezone | unsupported | gate | build | — | is_dst is not supported on ibis -- ibis has no DST/timezone-offset primitive to build on (verified 2026-08-16, ibis 12.0.0/duckdb) | — | — | 2026-08-16 | — | — | +| ibis-duckdb | timezone | — | iana_timezone | unsupported | gate | build | — | is_dst is not supported on ibis -- ibis has no DST/timezone-offset primitive to build on (verified 2026-08-16, ibis 12.0.0/duckdb) | — | — | 2026-08-16 | — | — | + ### `ROUND` × narwhals (FKEY_MOUNTAINASH_SCALAR_DATETIME) | Dialect | Param | Option values | Value class | Level | Enforcement | Boundary | Condition | Message | Workaround | Upstream | Since | Native errors | Probe-exempt | diff --git a/docs/reference/expression-coverage.json b/docs/reference/expression-coverage.json index 9d4a97c7..4d115178 100644 --- a/docs/reference/expression-coverage.json +++ b/docs/reference/expression-coverage.json @@ -1,7 +1,7 @@ { "stamp": { "declarations": 43, - "facts": 1460, + "facts": 1462, "operations": 326, "implementation_records": 978 }, @@ -39,8 +39,8 @@ "unknown": 0 }, "default_capable": 138, - "audited_clean": 112, - "constrained": 76, + "audited_clean": 111, + "constrained": 77, "audited_unknown": 0 } }, @@ -50,19 +50,19 @@ "expr_capable": 149, "literal_only": 65, "polymorphic": 9, - "unsupported": 1237 + "unsupported": 1239 }, "facts_by_enforcement": { - "gate": 1453, + "gate": 1455, "router_metadata": 3, "materialize_residue": 4 }, "facts_by_backend": { "polars": 265, "narwhals": 586, - "ibis": 609 + "ibis": 611 }, - "facts_total": 1460 + "facts_total": 1462 }, "families": [ { @@ -4101,15 +4101,48 @@ "impl_protocol": "MountainAshIbisScalarDatetimeExpressionSystem", "audited": true, "whole_op": null, - "constrained": false, + "constrained": true, "contradiction": false, "selector_counts": { - "params": 0, + "params": 1, "option_selectors": 0, - "value_classes": 0, - "dialects": 0 + "value_classes": 1, + "dialects": 1 }, - "constraints": [], + "constraints": [ + { + "dialect": null, + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "is_dst is not supported on ibis -- ibis has no DST/timezone-offset primitive to build on (verified 2026-08-16, ibis 12.0.0/duckdb)", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, + { + "dialect": "ibis-duckdb", + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "is_dst is not supported on ibis -- ibis has no DST/timezone-offset primitive to build on (verified 2026-08-16, ibis 12.0.0/duckdb)", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + } + ], "residue": [], "routed": [], "refinements": [] @@ -45276,6 +45309,22 @@ "native_errors": [], "probe_exempt": null }, + { + "dialect": null, + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "is_dst is not supported on ibis -- ibis has no DST/timezone-offset primitive to build on (verified 2026-08-16, ibis 12.0.0/duckdb)", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null + }, { "dialect": "ibis-duckdb", "param": "timezone", @@ -45291,6 +45340,22 @@ "since": "2026-07-29", "native_errors": [], "probe_exempt": null + }, + { + "dialect": "ibis-duckdb", + "param": "timezone", + "option_value": null, + "value_class": "iana_timezone", + "level": "unsupported", + "enforcement": "gate", + "boundary": "build", + "condition": null, + "message": "is_dst is not supported on ibis -- ibis has no DST/timezone-offset primitive to build on (verified 2026-08-16, ibis 12.0.0/duckdb)", + "workaround": null, + "upstream_ref": null, + "since": "2026-08-16", + "native_errors": [], + "probe_exempt": null } ] }, diff --git a/docs/reference/expression-coverage.md b/docs/reference/expression-coverage.md index c0c31b43..779f2694 100644 --- a/docs/reference/expression-coverage.md +++ b/docs/reference/expression-coverage.md @@ -3,7 +3,7 @@ -Declarations: 43 · Facts: 1460 · Registered operations: 326 · Implementation records: 978 +Declarations: 43 · Facts: 1462 · Registered operations: 326 · Implementation records: 978 Scoped deviations (dialect/param/option/value-class) live in [`expression-coverage-scoped.md`](expression-coverage-scoped.md). @@ -52,7 +52,7 @@ Legend — cell states (by exception): | --- | --- | --- | --- | --- | --- | --- | | polars | 191 | 83 | 52 | 0 | 0 | 326 | | narwhals | 97 | 151 | 78 | 0 | 0 | 326 | -| ibis | 138 | 112 | 76 | 0 | 0 | 326 | +| ibis | 138 | 111 | 77 | 0 | 0 | 326 | contradictions: 0 audited_unknown: 0 @@ -61,9 +61,9 @@ audited_unknown: 0 | Axis | Breakdown | | --- | --- | -| Level | expr_capable 149, literal_only 65, polymorphic 9, unsupported 1237 | -| Enforcement | gate 1453, router_metadata 3, materialize_residue 4 | -| Backend | polars 265, narwhals 586, ibis 609 | +| Level | expr_capable 149, literal_only 65, polymorphic 9, unsupported 1239 | +| Enforcement | gate 1455, router_metadata 3, materialize_residue 4 | +| Backend | polars 265, narwhals 586, ibis 611 | `pandas` / `pyarrow` are routed input types (they execute via the narwhals path) and are not independent coverage columns. @@ -162,7 +162,7 @@ audited_unknown: 0 | `EXTRACT_WEEKDAY` | ✓ | ✓ audited | ✓ audited | | `EXTRACT_YEAR` | ✓ | ✓ audited | ✓ audited | | `FLOOR` | ✓ | ◐ partial (1 params, 2 option-selectors, 0 value-classes, 2 dialects) | ◐ partial (1 params, 12 option-selectors, 0 value-classes, 1 dialects) | -| `IS_DST` | ✓ | ✓ audited | ✓ audited | +| `IS_DST` | ✓ | ✓ audited | ◐ partial (1 params, 0 option-selectors, 1 value-classes, 1 dialects) | | `IS_LEAP_YEAR` | ✓ | ✓ audited | ✓ audited | | `MONTH_END` | ✓ | ✓ audited | ✓ audited | | `MONTH_START` | ✓ | ✓ audited | ✓ audited |