Data validation configuration reference¶
This page documents the validation workflow YAML and shared Worker TOML settings for AIM DMV data validation. For platform-specific prerequisites, connectivity, and data type mappings, see the per-platform pages linked from Data validation.
Tip
If you use the Snowflake AIM Agent for Data Warehouses instead of hand-editing these files, see Data validation advanced configuration for common scenarios and the prompts that generate and adjust this configuration for you.
For SnowConvert AI CLI commands that generate and submit validation workflows, see Data validation.
Validation workflow file overview¶
Validation is driven by a single YAML workflow file. Key sections:
| Section | Purpose |
|---|---|
source_platform / target_platform | Source dialect and target (defaults to Snowflake) |
validation_configuration | Global L1/L2/L3 toggles, thresholds, early stopping, accepted transformations |
comparison_configuration | Numeric tolerance and optional type mapping file |
acceptedTransformations | Global rules for expected source-to-target value pairs (optional) |
database_mappings / schema_mappings | Source-to-target name maps |
tables | Tables to validate, with optional per-table overrides |
views | Same shape as tables, for view validation |
objects | Same shape as tables, with optional objectType; omitting the type triggers runtime detection |
Top-level object¶
| Property | Type | Required | Description |
|---|---|---|---|
source_platform | String | Yes | Source dialect: sqlserver, redshift, teradata, oracle, or postgresql. |
target_platform | String | Defaults to Snowflake. | |
target_database | String | Default target database for tables that don’t specify one. | |
affinity | String | Optional affinity tag that routes this workflow’s tasks to matching Workers. Same matching rules as migration; see Affinity. | |
validation_configuration | Object | Global validation levels and options. | |
comparison_configuration | Object | Numeric tolerance and optional type mapping file. | |
acceptedTransformations | Array | Global accepted-transformation rules. Merged with per-table rules. | |
database_mappings | Object | Map of source database names to Snowflake database names. | |
schema_mappings | Object | Map of source schema names to Snowflake schema names. | |
tables | Array | See note | Table entries to validate (each tagged object_type = "TABLE"). |
views | Array | See note | View entries using the same shape as tables (each tagged object_type = "VIEW"). |
objects | Array | See note | Entries using the same shape as tables, plus an optional objectType (TABLE or VIEW). Omitting objectType resolves the type at runtime against the target Snowflake catalog. See Objects with runtime type detection. |
target_partition_size_rows | Integer | Desired rows per partition. Mutually exclusive with target_partition_size_mb. | |
target_partition_size_mb | Integer | Desired MB per partition. Default is 200 MB when both are omitted. | |
use_snowpipe_for_results | Boolean | When true (default), L2/L3 results are ingested via Snowpipe. | |
queryModifiers | QueryModifiers | Optional SQL hints for source queries. Default is unset (no hints). See Anti-locking and query modifiers. | |
intervalHandling | String ("interval", "varchar") | How INTERVAL columns are compared. Defaults to "interval", matching the target’s native interval type. See INTERVAL data type handling. | |
validationCustomNormalizationRules | Array | Preferred, granular normalization overrides by data type, column, or column pattern. See Customizing normalization and metrics. | |
validationCustomNormalizations | Object | Legacy, workflow-wide overrides for L2/L3 normalization SQL templates, keyed only by data type. See Customizing normalization and metrics. | |
validationCustomMetrics | Object | Workflow-wide overrides for L2 metric definitions. See Customizing normalization and metrics. | |
validationCustomTypes | Object | Workflow-wide overrides for L1 source data type name mapping. See Customizing normalization and metrics. |
tables, views, and objects are each individually optional, but the workflow must define at least one entry across the three.
Objects with runtime type detection¶
The top-level objects array is an alternative to tables and views that lets you list objects without committing to a type. Each entry uses the same shape as a tables or views entry, plus an optional objectType:
- When
objectTypeisTABLEorVIEW, the entry behaves exactly like the correspondingtablesorviewsentry. - When
objectTypeis omitted, AIM DMV resolves the type at runtime against the target Snowflake catalog. OnlyTABLEandVIEWare supported. If a detected type is something else (for example a materialized view) or the object doesn’t exist on the target, only that object’s check fails; other objects continue.
tables, views, and objects can be combined freely in the same workflow.
Validation configuration¶
When validation_configuration is omitted, defaults are: schema validation and metrics validation enabled; row validation disabled; when L3 is enabled, validation fingerprints each partition with MD5 row hashes and drills into individual cells on mismatches; max_failed_rows_number defaults to 1000; early_stopping_for_cell_by_cell_comparison defaults to true; early_stopping_for_row_hashing defaults to false.
| Property | Type | Description |
|---|---|---|
schema_validation | Boolean | Level 1: schema and column consistency checks. |
metrics_validation | Boolean | Level 2: statistical metrics comparison. |
row_validation | Boolean | Level 3: row fingerprinting and cell drill-down on mismatches. |
continue_on_failure | Boolean | Whether to continue to the next validation level after a failure. |
max_failed_rows_number | Integer | Cap on failed rows reported for L3 per partition and early-stop threshold (default 1000). |
exclude_metrics | Boolean | When true, skips overflow-prone L2 aggregates: avg, sum, and stddev (default false). |
apply_metric_column_modifier | Boolean | When true (default), applies a platform overflow guard to aggregate metrics such as sum and avg. |
early_stopping_for_row_hashing | Boolean | When true, stops remaining row-hash partitions once max_failed_rows_number mismatches are ingested (default false). |
early_stopping_for_cell_by_cell_comparison | Boolean | When true, stops remaining cell drill-down partitions once max_failed_rows_number mismatches are ingested (default true). |
early_stop_check_interval_minutes | Integer | Poll interval when either early-stop flag is enabled (default 5). |
early_stop_check_interval_seconds | Integer | Alternative poll interval in seconds. Mutually exclusive with early_stop_check_interval_minutes. |
text_comparison_mode | String ("logical", "raw") | Teradata only. "logical" (default) normalizes text before comparing; "raw" compares source and target text byte-exact. |
acceptedTransformations | Array | Rules merged with workflow-root and per-table rules. |
Validation levels and result codes¶
Schema validation (L1) compares table name, column names, ordinal position, data types, character length, numeric precision and scale, nullability, and row count. Results use SUCCESS, WARNING (treated as a pass), or FAILURE.
Metrics validation (L2) compares row count, min, max, sum, average, null count, distinct count, standard deviation, and variance (metrics vary by column type). Numeric comparisons honor comparisonConfiguration.tolerance (default 0.001). Results use SUCCESS, WARNING, or FAILURE.
Row validation (L3) fingerprints each partition with MD5 row hashes, then drills into individual cells on mismatches. Row-level RESULT values include:
| Result | Meaning |
|---|---|
MISMATCH | The row matches on the index columns on both sides, but one or more compared values differ. |
POSSIBLE_MISMATCH | A provisional MISMATCH recorded while the table has accepted transformations configured. Reconciled before the workflow completes: rows matching an accepted rule are cleared, and the rest become MISMATCH. |
NOT_FOUND_TARGET | The row exists on the source but has no matching row on the target (missing from the target). |
NOT_FOUND_SOURCE | The row exists on the target but has no matching row on the source (extra row on the target). |
DUPLICATE_SOURCE | The index-column key appears more than once on the source side. |
DUPLICATE_TARGET | The index-column key appears more than once on the target side. |
DUPLICATE_BOTH_SIDES | The index-column key is duplicated on both the source and the target. |
Task-level failures are recorded in DATA_VALIDATION_ERROR, distinct from row-level MISMATCH results.
Comparison configuration¶
| Property | Type | Description |
|---|---|---|
tolerance | Number | Relative tolerance for L2 metric comparisons (default 0.001, or 0.1%). |
type_mapping_file_path | String | Optional path to a custom type mapping file. |
Accepted transformations¶
Accepted transformations whitelist specific source-to-target value pairs so AIM DMV does not report them as L3 mismatches. See Accepted transformations for the end-to-end flow and POSSIBLE_MISMATCH lifecycle.
Rules can appear at three levels (unioned per table):
- Workflow root (
acceptedTransformations) - Global
validation_configuration.acceptedTransformations - Per-table
acceptedTransformationsor nestedvalidation_configuration.acceptedTransformations
Each rule object:
| Property | Type | Required | Description |
|---|---|---|---|
column | String | One of column or columnPattern | Exact source column name (case-insensitive). |
columnPattern | String | One of column or columnPattern | Regex tested against the source column name. |
sourceValue | String or null | Yes | Expected source value. Use null to represent SQL NULL. |
targetValue | String or null | Yes | Expected target value after migration. |
Example:
Per-table and per-view entry¶
| Property | Type | Required | Description |
|---|---|---|---|
fully_qualified_name | String | Yes | Source object name (format depends on platform). |
use_column_selection_as_exclude_list | Boolean | Default false. | |
column_selection_list | String[] | Columns to include or exclude (literals and/or Python regex). | |
target_name | String | Target object name override. | |
target_database | String | Per-table target database override. | |
target_schema | String | Per-table target schema override. | |
where_clause | String | Filter on the source side. | |
target_where_clause | String | Filter on the target side. | |
index_column_list | String[] | Columns used to align rows on the source (required for L3). Can be omitted and inferred automatically. See Automatic partition and index key selection. | |
target_index_column_list | String[] | Columns used to align rows on the target. | |
column_mappings | Object | Map of source column name to target column name. | |
is_case_sensitive | Boolean | Case sensitivity for identifiers and column filtering (default false). | |
object_type | String | TABLE (default) or VIEW. Set to VIEW to validate a view from a flat tables list instead of the views array. | |
column_names_to_partition_by | String[] | Columns for range-based partitioning during L2/L3. Can be omitted and inferred automatically. See Automatic partition and index key selection. | |
target_partition_size_rows | Integer | Per-table rows per partition override. | |
target_partition_size_mb | Integer | Per-table MB per partition override. | |
max_failed_rows_number | Integer | Overrides the global L3 cap for this object. | |
acceptedTransformations | Array | Per-table accepted-transformation rules. | |
validation_configuration | Object | Nested overrides for this object only. | |
queryModifiers | QueryModifiers | Optional SQL hints for source queries on this object. Default is unset (no hints). See Anti-locking and query modifiers. | |
excludeMetrics | Boolean | Per-table override for exclude_metrics. | |
applyMetricColumnModifier | Boolean | Per-table override for apply_metric_column_modifier. | |
intervalHandling | String ("interval", "varchar") | Per-table override of the workflow-level intervalHandling setting. See INTERVAL data type handling. | |
validationCustomNormalizationRules | Array | Per-table normalization overrides. Take precedence over workflow-level rules for matching columns. See Customizing normalization and metrics. |
Column filtering with regex patterns¶
Each entry in column_selection_list is matched against every column name:
- Literal — plain string (for example
LOAD_DATE), case-insensitive unlessis_case_sensitive: true - Regex — entry wrapped in single quotes with
r"..."inside (for example'r".*_TS"')
use_column_selection_as_exclude_list | Behavior |
|---|---|
false (default) | Include mode — only matched columns are validated |
true | Exclude mode — all columns except matched ones are validated |
Partitioning¶
When column_names_to_partition_by is set, the Orchestrator splits the table into range-based partitions:
- Compute target rows-per-partition from
target_partition_size_rowsortarget_partition_size_mb(default 200 MB). - Apply internal caps for safe infrastructure bounds.
- Derive partition count as
ceil(row_count / effective_rows_per_partition).
Automatic partition and index key selection¶
When column_names_to_partition_by or index_column_list is omitted for a table that needs metrics or row validation, AIM DMV infers keys automatically from source catalog metadata:
- Partition key: clustered, sort, or distribution key columns when available, otherwise a unique index, otherwise the first non-boolean schema column as a last resort.
- Index key (for L3 row alignment): the declared primary key, otherwise the first unique index as a fallback.
Inferred keys are cached per source table and reused by later workflows. A manually specified column_names_to_partition_by or index_column_list always takes precedence. Views can’t be inferred; set these explicitly when validating a view, and prefer a partition key that matches the underlying tables plus a where_clause to limit the scan. See Validating views.
Each partition key must be a real, physical column name. Persisted computed columns (SQL Server) and virtual columns (Oracle) qualify. Bare SQL expressions, pseudo-columns (for example Oracle ROWID, ROWNUM, or ORA_ROWSCN), and hidden system columns are not valid: AIM DMV quotes partition key names as identifiers, so those values won’t resolve. To partition on a derived value, add a persisted or virtual computed column on the source and reference that column name.
Anti-locking and query modifiers¶
User-configured anti-locking hints are off by default. Unless you set queryModifiers, AIM DMV runs plain source queries. Modifiers apply to source queries only, never the Snowflake target. L3 row-hash validation uses the same source modifiers, so dirty reads can cause false MISMATCH results.
Source platforms fall into two groups:
- MVCC / snapshot engines (Oracle, PostgreSQL, Amazon Redshift, and Snowflake itself): readers don’t block writers, so anti-locking hints aren’t needed.
- Lock-based engines (SQL Server and Azure Synapse): shared locks on reads can block or be blocked by production writers. Opt-in hints such as
WITH (NOLOCK)avoid blocking but use read-uncommitted semantics (dirty reads). AIM DMV never auto-applies these hints. Dirty reads can produce falseMISMATCHresults during validation; the impact is usually stronger at L3 (byte-exact row hashing) than at L2 (aggregate metrics compared within tolerance).
Teradata is a special case: AIM DMV prepends LOCKING ROW FOR ACCESS on every Teradata source scan automatically. This is Teradata’s native, safe row-access pattern (not a dirty read) and cannot be disabled.
Set queryModifiers at the workflow root or on individual table or view entries. See the QueryModifiers model in the Data migration configuration reference.
Per-platform behavior¶
| Platform | Default behavior | How to customize |
|---|---|---|
| Teradata | LOCKING ROW FOR ACCESS added automatically on every source scan | Not configurable |
| SQL Server | No automatic hint | Set objectModifier: " WITH (NOLOCK)" at workflow root or per table |
| Oracle | Automatic PARALLEL optimizer hint on large tables | Override with selectModifier, or set selectModifier: "NONE" to disable |
| Amazon Redshift | No automatic hint | Opt-in pass-through via queryModifiers |
| PostgreSQL | No automatic hint | Opt-in pass-through via queryModifiers |
Pros and cons¶
| Pros | Source reads no longer block on or wait for locks held by production writers, so validation isn’t stalled by long-running source transactions. On Oracle, the parallel hint can speed large-table scans. |
| Cons | WITH (NOLOCK) (and equivalent read-uncommitted hints) allow dirty reads: scans can see uncommitted rows that later roll back, which can produce false MISMATCH results during validation. The impact is usually stronger at L3 (byte-exact row hashing) than at L2 (aggregate metrics within tolerance). Many DBAs disallow NOLOCK. Custom modifier strings are passed through verbatim, so an invalid hint can cause source query errors. |
| Recommendation | Leave hints off unless source locking is actually blocking your workflow. When you do enable them, prefer running validation against a quiesced or low-write window. |
Workflow example¶
Worker TOML example¶
Warning
WITH (NOLOCK) uses read-uncommitted semantics. During validation, dirty reads can produce false MISMATCH results that do not reflect the committed source data.
INTERVAL data type handling¶
intervalHandling controls how INTERVAL columns are compared, and should match the value used for the same table during migration. Neither setting is universally better: "interval" compares as a native Snowflake INTERVAL (on PostgreSQL, mixed year-month and day-time values were already folded into INTERVAL DAY TO SECOND during migration), and "varchar" compares as text when migration preserved the original interval text. See INTERVAL columns and intervalHandling for the PostgreSQL tradeoff.
| Value | Behavior |
|---|---|
"interval" (default) | Compare the column as a native Snowflake INTERVAL value against the normalized source interval text. |
"varchar" | Compare the column as text, matching a table migrated with intervalHandling: "varchar". |
Set intervalHandling at the workflow root or on a per-table entry. See INTERVAL data type handling in the Data migration configuration reference for how values are extracted and cast during migration.
Note
Comparing an INTERVAL column with the wrong intervalHandling value (mismatched with how it was migrated) produces false MISMATCH results, because the two sides are normalized differently.
Customizing normalization and metrics¶
AIM DMV ships built-in normalization and metric templates per platform and data type. The keys below override those defaults so benign formatting differences (for example float precision, or a platform-specific type such as Teradata PERIOD) don’t show up as L3 mismatches. Set these in the validation workflow file, not in Worker TOML.
Custom normalization rules (validationCustomNormalizationRules)¶
validationCustomNormalizationRules is the preferred way to override normalization. Unlike the legacy validationCustomNormalizations key (still supported, see below), rules can target a specific column or column name pattern, not only a data type, and can be scoped to the workflow root or to an individual table.
Each rule:
| Property | Type | Required | Description |
|---|---|---|---|
dataType | String | Exactly one of dataType, column, columnPattern | Applies the rule to every column of this data type. |
column | String | Exactly one of dataType, column, columnPattern | Applies the rule to one exact column name. |
columnPattern | String | Exactly one of dataType, column, columnPattern | Applies the rule to column names matching this regex. |
sourceExpression | String | At least one of sourceExpression, targetExpression | SQL template in the source dialect. Use the {{ col_name }} placeholder for the column reference. |
targetExpression | String | At least one of sourceExpression, targetExpression | SQL template in Snowflake SQL. Use the {{ col_name }} placeholder for the column reference. |
When more than one rule could match the same column, AIM DMV applies the most specific one, in this order: table-level column match, table-level columnPattern match, table-level dataType match, workflow-level column match, workflow-level columnPattern match, workflow-level dataType match, then the built-in template for that platform and type.
Use custom normalization rules when a benign, predictable formatting difference (not a real data problem) would otherwise show up as an L3 mismatch, and you want the fix scoped to one column, one naming pattern, or one table rather than every column of a data type.
Custom normalization (validationCustomNormalizations, legacy)¶
Normalization is the SQL AIM DMV wraps around each column so source and target values compare in a canonical form (consistent date, number, or string formatting) for L2 metrics and L3 hashing. This key is global-only and keyed by data type; prefer validationCustomNormalizationRules for new workflows, especially when you need per-column or per-table control. When both are present for the same column, the granular rule wins.
| Property | Type | Description |
|---|---|---|
source | Array | Overrides applied on the source platform side. |
target | Array | Overrides applied on the Snowflake target side. |
Each array entry is a single-key map of data type → SQL template. Keys are data types (uppercased), not column names. Use the "{{ col_name }}" placeholder for the column reference.
An override for a data type replaces the built-in template for that type; other types are unchanged.
Custom metrics (validationCustomMetrics)¶
L2 metrics are chosen by column data type. A DEFAULT set applies to types without a specific entry.
| Property | Type | Description |
|---|---|---|
source | Array | Metric overrides on the source platform side. |
target | Array | Metric overrides on the Snowflake target side. |
Each array entry:
| Field | Type | Description |
|---|---|---|
datatype | String | Source or target data type (uppercased). |
metrics | Array | Metric definitions for that type. |
replace_all | Boolean | When true, removes all built-in metrics for the type before applying the listed metrics (default false). |
Each metric definition:
| Field | Type | Description |
|---|---|---|
name | String | Metric name (for example count, sum, min, max, stddev). |
metric_query | String | SQL aggregate using "{{ col_name }}". A null or empty value removes that metric from the built-in set. |
metric_return_datatype | String | Data type used to normalize the metric result. |
metric_column_modifier | String | Optional overflow guard override for this metric. |
Control which metrics run with these related settings:
| Setting | Default | Purpose |
|---|---|---|
metrics_validation | true | Enable or disable L2 entirely (global or per table). |
exclude_metrics / excludeMetrics | false | Skip overflow-prone aggregates: avg, sum, stddev. |
apply_metric_column_modifier / applyMetricColumnModifier | true | Apply platform overflow guards to aggregate metrics. |
comparisonConfiguration.tolerance | 0.001 | Relative threshold for numeric L2 metric comparison only. |
Example:
L1 type mapping overrides (validationCustomTypes)¶
validationCustomTypes overrides how source data type names map for L1 schema comparison. This is distinct from normalization (L2/L3).
| Property | Type | Description |
|---|---|---|
source | Array | List of single-key maps, each from an original source type name to the normalized type name. |
Example:
Worker configuration¶
Workers reuse the same TOML format as Data Migration. See Data migration configuration reference for the full property list, including custom extraction plugin setup.
Workers that execute validation tasks must have the validation runtime available. The [connections.source.*] section matches the source platform documented on each Validating Data from … page.
Observability¶
Validation metadata lives under SNOWCONVERT_AI.DATA_VALIDATION by default. Filter queries by WORKFLOW_ID. See The SNOWCONVERT_AI database for tables, views, workflow management procedures, and sample queries.