Skip to content

OBSL-1019/1007 (soda-core part): raw feature-config passthroughs on the dataset-config DTO + MM/dialect seams#2777

Open
mivds wants to merge 31 commits into
obsl-1027-sql-ast-window-functions-and-cte-unionfrom
obsl-1019-profiling-config-dto
Open

OBSL-1019/1007 (soda-core part): raw feature-config passthroughs on the dataset-config DTO + MM/dialect seams#2777
mivds wants to merge 31 commits into
obsl-1027-sql-ast-window-functions-and-cte-unionfrom
obsl-1019-profiling-config-dto

Conversation

@mivds

@mivds mivds commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

PR chain (soda-core)

  1. OBSL-1013: ship table discovery in soda-core (soda data-source discover) #2773 — ship table/column discovery
  2. OBSL-1025: emit metrics[] in scan-results payload when measurements present #2774 — emit metrics[] in scan-results payload
  3. OBSL-1027: SQL AST window functions + UNION-in-CTE #2776 — SQL AST window functions + UNION-in-CTE
  4. OBSL-1019/1007 (soda-core part): raw feature-config passthroughs on the dataset-config DTO + MM/dialect seams #2777 — raw feature-config passthrough (config DTO) ← this PR
  5. All-datasource dialect groundwork: time/interval, percentile, typed timestamp literal, NaN filter #2787 — all-datasource dialect groundwork

Summary

Cloud/dialect seams for the observability extensions (profiling, metric monitoring), stacked on #2776 (OBSL-1027).

Dataset-config DTO: raw feature-config passthroughs

  • DatasetConfigurationDTO.profiling_configuration (alias profilingConfiguration) is a raw Optional[dict] passthrough: the soda-profiling extension (soda_profiling.profiling_config, soda-extensions) owns the parsing, so soda-core stays free of profiling semantics — the same convention as metricMonitoringConfiguration (metric-monitoring's monitor_config) and timePartitionConfiguration (the partition detector). Wire JSON unchanged; the dict arrives and round-trips verbatim. The earlier typed ProfilingConfigurationDTO + sampling-strategy union from this branch was removed again in favor of the passthrough; the typed models now live in soda-extensions.
  • metric_monitoring_configuration raw list[dict] field + historic-data queries + identity helpers (MM transport seams, OBSL-1007) and get_last_measurement single-identity historic query (OBSL-1028).
  • Per-CheckResult cloud-dict override hook for non-contract check shapes (OBSL-1008).

SQL AST + dialect seams

  • TIME_DELTA + ADD_INTERVAL time-bucket nodes with snowflake/bigquery renderers (OBSL-1028).
  • PERCENTILE_WITHIN_GROUP ordered-set aggregate node + bigquery APPROX_QUANTILES renderer (OBSL-1022).
  • sql_expr_timestamp_coerce dialect hook for partition-window comparisons and get_large_numeric_cast_type_name capability for high-precision aggregate math (OBSL-1005).
  • Fixes: BigQuery renders UNION as UNION ALL; BigQuery DATETIME/BIGNUMERIC + parameterized types in the soda-type reverse map; defensive Snowflake string-type aliases (OBSL-1005).

soda-core part of OBSL-1019 and the MM/dialect seams it stacks with. The profiling orchestrator + soda profile CLI (and the typed profiling-config models) are the soda-extensions part (sodadata/soda-extensions#457).

Test Plan

  • Unit: profilingConfiguration passes through as a raw dict via alias, round-trips by alias unchanged, omitted key parses to None (no regression); typed parsing is covered in soda-extensions (soda-profiling/tests/test_profiling_config.py)
  • pytest soda-tests/tests/unit -q: 1059 passed, 1 skipped; pre-commit clean

🤖 Generated with Claude Code

mivds and others added 9 commits July 1, 2026 01:02
Add ProfilingConfigurationDTO (pydantic BaseModel, extra=allow) with
is_enabled and sampling_strategy_configuration fields; wire it into
DatasetConfigurationDTO replacing the commented-out dict placeholder.
All fields Optional so existing payloads without profilingConfiguration
parse unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n on dataset config DTO (OBSL-1018)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…p type parameters from metadata (OBSL-1005)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…p (OBSL-1005)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…) (OBSL-1005)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…recision aggregate math (OBSL-1005)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mparisons (OBSL-1005)

Base SqlDialect: identity — postgres/duckdb/snowflake implicitly coerce ISO
string window-bound literals against temporal columns (verified live on all
three). BigQuerySqlDialect: timestamp({expr}) — BigQuery coerces the string
literal to the COLUMN's type, and an ISO string with a UTC offset cannot
cast to a naive DATETIME column ('Could not cast literal
"...T12:00:00+00:00" to type DATETIME', verified live). v3 provenance:
get_time_between_sql TIMESTAMP({column}) BETWEEN ... (soda-library
bigquery_data_source.py:465-467); like v3 the wrap is unconditional on
BigQuery and is a no-op on TIMESTAMP columns (verified live).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mivds mivds self-assigned this Jul 4, 2026
mivds and others added 9 commits July 5, 2026 01:38
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…igquery APPROX_QUANTILES renderer (OBSL-1022)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… queries, identity helpers (OBSL-1007)

- DatasetConfigurationDTO.metric_monitoring_configuration: raw list[dict] on
  purpose (metricMonitoringConfiguration); the metric-monitoring plugin's
  monitor_config owns parsing, like timePartitionConfiguration.
- soda_core/common/observability_identity.py: get_metric_identity /
  get_check_identity ported byte-for-byte from v3 (profiling_metric.py:113-132,
  observability config.py:145-147); pins sourced by executing the actual v3
  implementation in the pinned soda-library venv.
- SodaCloud.get_historic_measurements / get_historic_check_results: v3
  sodaCoreHistoricMeasurements2 / sodaCoreHistoricCheckResults2 queries with
  minScanTime/maxScanTime XOR limit, v3 millisecond-UTC scan-time strings, and
  batching+merge over the 500-identities-per-request BE constraint.
- MockSodaCloud: fixture-driven historic-data responses keyed by identity;
  400s oversized identity lists to keep the batching tests honest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lake/bigquery renderers (OBSL-1028)

Base renderers are v3's postgres forms (epoch-floor partition index,
interval-multiply scan_time reconstruction) — byte-parity on postgres,
valid duckdb, unit-safe for the fixed-length units weeks/days/hours/seconds.
Snowflake keeps v3's TIMESTAMPDIFF-in-seconds form; bigquery keeps
TIMESTAMP_DIFF/TIMESTAMP_ADD with singular unit names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…L-1028)

v3 soda_cloud.py:703-712 verbatim semantics: sodaCoreHistoricMeasurements2
with limit 1, first-result-or-None; shares the OBSL-1007 historic-data
fetch plumbing and mock/test pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t check shapes (OBSL-1008)

CheckResult.build_soda_cloud_check_dict(contract, wire_source) defaults to
None; _build_check_results_cloud_json_dicts uses a non-None return verbatim,
else falls back to the generic _build_check_result_cloud_dict. Contract check
dicts are byte-unchanged (pinned by unit test); metric monitoring subclasses
CheckResult to emit v3-parity anomalyDetection check dicts through the same
send_check_collection_results path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ig DTO

The soda-profiling extension (soda_profiling.profiling_config) owns the
parsing of profilingConfiguration, so soda-core stays free of profiling
semantics — the same convention as metricMonitoringConfiguration
(metric-monitoring's monitor_config) and timePartitionConfiguration
(the partition detector). Wire JSON is unchanged: the dict arrives and
round-trips verbatim.

Removes ProfilingConfigurationDTO and the sampling-strategy union types
(CardinalitySamplingStrategyConfigurationDTO,
TimePartitionSamplingStrategyConfigurationDTO,
SamplingStrategyConfiguration); their typed tests move to
soda-profiling alongside the ported models. (OBSL-1019)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mivds mivds changed the title OBSL-1019 (part A): type ProfilingConfigurationDTO OBSL-1019/1007 (soda-core part): raw feature-config passthroughs on the dataset-config DTO + MM/dialect seams Jul 11, 2026
mivds and others added 10 commits July 12, 2026 12:58
CheckCollectionImpl.log_table_extra_columns(check_result) -> dict, default {}
so every existing collection type's table (contract verification, regular
metric monitoring) stays byte-identical — pinned by the existing
test_contract_verification_logs byte-exact lines plus an explicit
no-Window guard.

Overriding subtypes (the soda-extensions metric-monitoring BACKFILL
collection, whose N rows per check are otherwise visually identical — one
per backfilled window) return e.g. {"Window": "2026-07-09"}: new keys
become columns inserted right after Check and participate in the row sort
at that position; keys that already exist in the row (e.g. Diagnostics)
replace that cell's value in place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Second seam next to log_table_extra_columns: CheckCollectionImpl.
log_table_header_overrides() -> dict[str, str] (default {}) lets a
collection subtype rename rendered table headers, e.g. metric
monitoring renders the "Check" column as "Monitor". The rename is
applied by build_summary_table only at render time, so row-dict keys
stay internal: sorting and log_table_extra_columns cell overrides keep
targeting the internal names. Default {} keeps contract verification
output byte-identical (byte-identity pins extended).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@m1n0

m1n0 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Reviewed (aware it's still draft) as a mixed PR, per group. Most of it is clean: DTO passthroughs round-trip by alias and the ownership rationale is documented; observability_identity digests are pinned and the add-order is called out as load-bearing; the historic-query trio (XOR validation, 500-identity batching, grouping) is well-tested; and both override hooks (build_soda_cloud_check_dict, summary-table extra columns/headers) provably leave the default contract paths unchanged. CI green.

One finding I'd fix before undrafting:

  • BigQuery build_union_sql renders plain UNION as UNION ALL (bigquery_data_source.py:174). The docstring's justification is profiling-specific ("disjoint constant metric_ labels"), but this is a general dialect method: any future caller building UNION(...) on BigQuery expecting set semantics silently gets duplicates. BigQuery supports UNION DISTINCT — rendering UNIONUNION DISTINCT and UNION_ALLUNION ALL is both valid BQ SQL and semantics-preserving, and callers that want the cheaper form can say UNION_ALL explicitly.

Smaller notes:

  • get_historic_measurements/get_historic_check_results with limit + >500 identities applies the limit per batch — merged results can exceed it. Current callers won't hit it (limit is used with single identities), but worth a line in the docstring or a guard.
  • Snowflake _build_time_delta_sql counts crossed second-boundaries (TIMESTAMPDIFF(second, ...)) while the base form measures true duration — sub-second window bounds can bucket off-by-one vs postgres. Immaterial if MM scan times are whole-second aligned; flagging the boundary.
  • snowflake_data_source.py:185: the new get_large_numeric_cast_type_name landed between the pre-existing "TODO: test this thorough / AI-generated" comment and the type-map function it describes — the TODO now reads as if it's about the new method.
  • TIME_BUCKET_UNITS and BigQuery's _TIME_BUCKET_UNIT_NAMES are two sources of truth; adding a unit to the former silently KeyErrors at BQ render time.

- BigQuerySqlDialect.build_union_sql: render plain UNION as UNION DISTINCT
  (was UNION ALL), preserving set semantics for any caller; UNION_ALL still
  renders UNION ALL. Update tests accordingly.
- Snowflake: move the "AI-generated / test thorough" TODO back above the
  type-map function it describes (a new method had been inserted between them).
- get_historic_measurements/get_historic_check_results: document that `limit`
  is applied per identity-batch, so multi-batch merged results can exceed it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mivds
mivds marked this pull request as ready for review July 20, 2026 08:23
@mivds
mivds requested a review from a team July 20, 2026 08:23
@mivds

mivds commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Addressed in 23a9ae9:

  • BigQuery build_union_sql — plain UNION now renders UNION DISTINCT (was UNION ALL), preserving set semantics for any caller; UNION_ALL still renders UNION ALL. Tests updated to pin both.
  • Snowflake TODO placement — moved the "AI-generated / test thorough" TODO back above the type-map function it describes (the new get_large_numeric_cast_type_name had been inserted between them).
  • Per-batch limit — documented on both get_historic_measurements/get_historic_check_results that limit is applied per identity-batch, so a multi-batch merged result can exceed it (current callers pass a limit only with single identities).

Deferred (noted, not changed): the Snowflake _build_time_delta_sql second-boundary note is immaterial for whole-second MM scan times; the TIME_BUCKET_UNITS vs _TIME_BUCKET_UNIT_NAMES two-sources-of-truth is the same cross-dialect consolidation you flagged on #2787 — tracking it there as one follow-up rather than touching ~6 dialects in this PR.

… package

Metric/check identity generation is a cross-feature observability concern
(profiling + metric monitoring), and soda-core neither generates nor consumes
these identities — nothing here imported observability_identity. Only the
generic ConsistentHashBuilder primitive belongs in soda-core.

The helpers + pin test now live in the shared `soda` extensions package
(soda.observability.identity), which both soda-profiling and
soda-metric-monitoring already depend on. See soda-extensions
obsl-1037-profiling-all-datasources.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mivds added a commit that referenced this pull request Jul 20, 2026
… soda package)

Mirrors the removal on #2777: soda-core neither generates nor consumes metric/
check identities. The helpers + pin test live in soda.observability.identity in
the shared `soda` extensions package. Keeps this branch's tree consistent so the
file never reaches main via the chain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants