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.

Note

Property names in this file are camelCase (for example sourcePlatform, fullyQualifiedName, validationConfiguration). This is the format scai data validate generate-config produces and scai data validate start reads. Keys that don’t match — including snake_case spellings such as source_platform — are silently ignored, which can leave a required value like sourcePlatform unset and stop the workflow from starting. When in doubt, generate the file with scai data validate generate-config and edit from there.

Validation workflow file overview

Validation is driven by a single YAML workflow file. Key sections:

SectionPurpose
sourcePlatform / targetPlatformSource dialect and target (defaults to Snowflake)
validationConfigurationGlobal L1/L2/L3 toggles, thresholds, early stopping, accepted transformations
comparisonConfigurationNumeric tolerance and optional type mapping file
acceptedTransformationsGlobal rules for expected source-to-target value pairs (optional)
databaseMappings / schemaMappingsSource-to-target name maps
tablesTables to validate, with optional per-table overrides
viewsSame shape as tables, for view validation
objectsSame shape as tables, with optional objectType; omitting the type triggers runtime detection

Top-level object

PropertyTypeRequiredDescription
sourcePlatformStringYesSource dialect: sqlserver, redshift, teradata, oracle, postgresql, or snowflake.
targetPlatformStringDefaults to Snowflake.
targetDatabaseStringDefault target database for tables that don’t specify one.
affinityStringOptional affinity tag that routes this workflow’s tasks to matching Workers. Same matching rules as migration; see Affinity.
validationConfigurationObjectGlobal validation levels and options.
comparisonConfigurationObjectNumeric tolerance and optional type mapping file.
acceptedTransformationsArrayGlobal accepted-transformation rules. Merged with per-table rules.
databaseMappingsObjectMap of source database names to Snowflake database names.
schemaMappingsObjectMap of source schema names to Snowflake schema names.
tablesArraySee noteTable entries to validate (each tagged objectType = "TABLE").
viewsArraySee noteView entries using the same shape as tables (each tagged objectType = "VIEW").
objectsArraySee noteEntries 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.
targetPartitionSizeRowsIntegerDesired rows per partition. Mutually exclusive with targetPartitionSizeMb.
targetPartitionSizeMbIntegerDesired MB per partition. Default is 200 MB when both are omitted.
useSnowpipeForResultsBooleanWhen true (default), L2/L3 results from Worker-based workflows are ingested via Snowpipe. Snowflake-to-Snowflake workflows ignore this flag and never use Snowpipe.
queryModifiersQueryModifiersOptional SQL hints for source queries. Default is unset (no hints). See Anti-locking and query modifiers.
intervalHandlingString ("interval", "varchar")How INTERVAL columns are compared. Defaults to "interval", matching the target’s native interval type. See INTERVAL data type handling.
validationCustomNormalizationRulesArrayPreferred, granular normalization overrides by data type, column, or column pattern. See Customizing normalization and metrics.
validationCustomNormalizationsObjectLegacy, workflow-wide overrides for L2/L3 normalization SQL templates, keyed only by data type. See Customizing normalization and metrics.
validationCustomMetricsObjectWorkflow-wide overrides for L2 metric definitions. See Customizing normalization and metrics.
validationCustomTypesObjectWorkflow-wide overrides for L1 source data type name mapping. See Customizing normalization and metrics.
validationCustomTypeRulesArrayPer-column L1 type overrides, for columns deliberately migrated to a different type. See Per-column L1 type overrides.
defaultTableConfigurationObjectShared defaults inherited by every table, view, and object entry. Per-entry properties override them, and a nested synchronization block is merged field by field. See Incremental validation.
synchronizationSynchronizationStrategyEnables incremental validation, which re-validates only changed partitions. Usually set once under defaultTableConfiguration. See Incremental validation.
cleanUpTransientResourcesString ("never", "on-success", "always")When to delete this workflow’s intermediate stage files under the validation TASK_RESULTS stage. Defaults to "on-success". Underscores are accepted, so on_success also works. Set "never" to keep the files while debugging. See Cleaning up transient resources.

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 objectType is TABLE or VIEW, the entry behaves exactly like the corresponding tables or views entry.
  • When objectType is omitted, AIM DMV resolves the type at runtime against the target Snowflake catalog. Only TABLE and VIEW are 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 validationConfiguration is omitted, defaults are: schema validation and metrics validation enabled; row validation disabled; when L3 is enabled, Worker-based workflows fingerprint each partition with MD5 row hashes and drill into individual cells on mismatches (Snowflake-to-Snowflake L3 uses SQL set-difference instead; see Validating Data from Snowflake); maxFailedRowsNumber defaults to 1000; earlyStoppingForCellByCellComparison defaults to true; earlyStoppingForRowHashing defaults to false.

PropertyTypeDescription
schemaValidationBooleanLevel 1: schema and column consistency checks.
metricsValidationBooleanLevel 2: statistical metrics comparison.
rowValidationBooleanLevel 3: row fingerprinting and cell drill-down on mismatches.
continueOnFailureBooleanWhether to continue to the next validation level after a failure.
maxFailedRowsNumberIntegerCap on failed rows reported for L3 per partition and early-stop threshold (default 1000).
excludeMetricsBooleanWhen true, skips overflow-prone L2 aggregates: avg, sum, and stddev (default false).
applyMetricColumnModifierBooleanWhen true (default), applies a platform overflow guard to aggregate metrics such as sum and avg.
earlyStoppingForRowHashingBooleanWhen true, stops remaining row-hash partitions once maxFailedRowsNumber mismatches are ingested (default false).
earlyStoppingForCellByCellComparisonBooleanWhen true, stops remaining cell drill-down partitions once maxFailedRowsNumber mismatches are ingested (default true).
earlyStopCheckIntervalMinutesIntegerPoll interval when either early-stop flag is enabled (default 5).
earlyStopCheckIntervalSecondsIntegerAlternative poll interval in seconds. Mutually exclusive with earlyStopCheckIntervalMinutes.
textComparisonModeString ("logical", "raw")Teradata only. "logical" (default) normalizes text before comparing; "raw" compares source and target text byte-exact.
onUntranslatableString ("substitute", "fail")Teradata only. How untranslatable characters in non-Latin Teradata character sets (for example KANJISJIS, GRAPHIC) are handled when the source is translated to Unicode for L3 row hashing. "substitute" (default) replaces each untranslatable character so comparison proceeds; "fail" fails the affected task instead. Set globally or per table. See Character set handling.
acceptedTransformationsArrayRules 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) on Worker-based (non-Snowflake) workflows fingerprints each partition with MD5 row hashes, then drills into individual cells on mismatches. Snowflake-to-Snowflake L3 compares rows with SQL set-difference in the warehouse. Row-level RESULT values include:

ResultMeaning
MISMATCHThe row matches on the index columns on both sides, but one or more compared values differ.
POSSIBLE_MISMATCHA 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_TARGETThe row exists on the source but has no matching row on the target (missing from the target).
NOT_FOUND_SOURCEThe row exists on the target but has no matching row on the source (extra row on the target).
DUPLICATE_SOURCEThe index-column key appears more than once on the source side.
DUPLICATE_TARGETThe index-column key appears more than once on the target side.
DUPLICATE_BOTH_SIDESThe 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

PropertyTypeDescription
toleranceNumberRelative tolerance for L2 metric comparisons (default 0.001, or 0.1%).
typeMappingFilePathStringOptional path to a custom type mapping file.

Accepted transformations

Accepted transformations allowlist 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):

  1. Workflow root (acceptedTransformations)
  2. Global validationConfiguration.acceptedTransformations
  3. Per-table acceptedTransformations or nested validationConfiguration.acceptedTransformations

Each rule object:

PropertyTypeRequiredDescription
columnStringOne of column or columnPatternExact source column name (case-insensitive).
columnPatternStringOne of column or columnPatternRegex tested against the source column name.
sourceValueString or nullYesExpected source value. Use null to represent SQL NULL.
targetValueString or nullYesExpected target value after migration.

Example:

validationConfiguration:
  rowValidation: true
acceptedTransformations:
  - column: status
    sourceValue: "ACTIVE"
    targetValue: "1"
  - columnPattern: "^flag_"
    sourceValue: null
    targetValue: "false"
tables:
  - fullyQualifiedName: MYDB.MYSCHEMA.MYTABLE
    acceptedTransformations:
      - column: code
        sourceValue: "Y"
        targetValue: "YES"

Per-table and per-view entry

Property naming and aliases

Some per-entry properties accept more than one spelling. Use the documented camelCase name in new workflows; the other spellings are kept for compatibility with existing files.

ConceptDocumented nameAlso accepted
Source row filtersourceWhereClausewhereClause (legacy)
Target row filtertargetWhereClause—
L3 index columns (source)indexColumnListindex_column_list
L3 index columns (target)targetIndexColumnListtarget_index_column_list
Partition columnscolumnNamesToPartitionBypartitionColumn (legacy, single column)
Rows per partitiontargetPartitionSizeRowstargetRowsPerPartition (legacy)
MB per partitiontargetPartitionSizeMbtargetMbPerPartition (legacy)

Only the spellings listed above are recognized. Any other key — including snake_case forms not shown here, such as where_clause or column_names_to_partition_by — is silently ignored rather than rejected, so a typo leaves the corresponding value unset.

Three rules apply:

  • Set both WHERE clauses or neither. Filtering one side only means you’re comparing different row subsets on source and target, which almost always reports mismatches.
  • Don’t combine sourceWhereClause with the legacy whereClause on the same entry. The workflow is rejected when it loads.
  • camelCase wins if an entry supplies both indexColumnList and its index_column_list alias (the same applies to targetIndexColumnList).
PropertyTypeRequiredDescription
fullyQualifiedNameStringYesSource object name (format depends on platform).
useColumnSelectionAsExcludeListBooleanDefault false.
columnSelectionListString[]Columns to include or exclude (literals and/or Python regex).
targetNameStringTarget object name override.
targetDatabaseStringPer-table target database override.
targetSchemaStringPer-table target schema override.
sourceWhereClauseStringFilter applied to source rows, in the source dialect. Pair it with targetWhereClause. See Property naming and aliases and Filtering compared rows.
targetWhereClauseStringFilter applied to target rows, in Snowflake SQL. Pair it with sourceWhereClause.
indexColumnListString[]Columns used to align rows on the source (required for L3). Can be omitted and inferred automatically. See Automatic partition and index key selection.
targetIndexColumnListString[]Columns used to align rows on the target.
columnMappingsObjectMap of source column name to target column name.
isCaseSensitiveBooleanCase sensitivity for identifiers and column filtering (default false).
objectTypeStringTABLE (default) or VIEW. Set to VIEW to validate a view from a flat tables list instead of the views array.
columnNamesToPartitionByString[]Columns for range-based partitioning during L2/L3. Can be omitted and inferred automatically. See Automatic partition and index key selection.
targetPartitionSizeRowsIntegerPer-table rows per partition override.
targetPartitionSizeMbIntegerPer-table MB per partition override.
maxFailedRowsNumberIntegerOverrides the global L3 cap for this object.
acceptedTransformationsArrayPer-table accepted-transformation rules.
validationConfigurationObjectNested overrides for this object only.
queryModifiersQueryModifiersOptional SQL hints for source queries on this object. Default is unset (no hints). See Anti-locking and query modifiers.
excludeMetricsBooleanPer-table override for excludeMetrics.
applyMetricColumnModifierBooleanPer-table override for applyMetricColumnModifier.
intervalHandlingString ("interval", "varchar")Per-table override of the workflow-level intervalHandling setting. See INTERVAL data type handling.
validationCustomNormalizationRulesArrayPer-table normalization overrides. Take precedence over workflow-level rules for matching columns. See Customizing normalization and metrics.
synchronizationSynchronizationStrategyPer-table incremental validation strategy. Overrides defaultTableConfiguration.synchronization field by field. See Incremental validation.

Filtering compared rows

sourceWhereClause and targetWhereClause limit which rows take part in validation. Each filter is stored per side and AND-composed with the partition range predicate when L2 and L3 run, so a filter never widens a partition’s scope. Source-side SQL is written in the source dialect, while target-side SQL goes through column mapping and identifier folding before it runs on Snowflake.

tables:
  - fullyQualifiedName: MYDB.dbo.ORDERS
    targetName: ORDERS
    sourceWhereClause: "STATUS = 'ACTIVE'"
    targetWhereClause: "STATUS = 'ACTIVE'"
    indexColumnList:
      - ORDER_ID

A targetWhereClause that filters out every row is not treated as an empty target table: partition sizing continues against the filtered count. A genuinely empty target behaves differently, running L1 if enabled and skipping L2 and L3 with an explanatory failure row.

When a table uses incremental validation, neither filter is applied while detecting change. Change probes read partition boundaries only. The filters apply when L2 and L3 run on a partition that changed.

Column filtering with regex patterns

Each entry in columnSelectionList is matched against every column name:

  • Literal — plain string (for example LOAD_DATE), case-insensitive unless isCaseSensitive: true
  • Regex — entry wrapped in single quotes with r"..." inside (for example 'r".*_TS"')
useColumnSelectionAsExcludeListBehavior
false (default)Include mode — only matched columns are validated
trueExclude mode — all columns except matched ones are validated

Partitioning

When columnNamesToPartitionBy is set, the Orchestrator splits the table into range-based partitions:

  1. Compute target rows-per-partition from targetPartitionSizeRows or targetPartitionSizeMb (default 200 MB).
  2. Apply internal caps for safe infrastructure bounds.
  3. Derive partition count as ceil(row_count / effective_rows_per_partition).

Automatic partition and index key selection

When columnNamesToPartitionBy or indexColumnList 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 columnNamesToPartitionBy or indexColumnList 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 sourceWhereClause 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.

Index keys deserve particular attention when you enable L3. They’re what lets AIM DMV pair a source row with its target row, so they need to identify rows uniquely: a non-unique index key produces DUPLICATE_SOURCE and DUPLICATE_TARGET results rather than useful comparisons. Set targetIndexColumnList as well whenever the target column names differ from the source, for example after a columnNameMappings rename during migration.

Incremental validation

By default, every validation run compares every partition of every table. Incremental validation detects which partitions changed since the last run and re-validates only those, which cuts cost and runtime on scheduled re-validation of large tables.

Incremental validation uses the same synchronization block as incremental sync in data migration, but it’s read-only: nothing is written to the target, and no rows are moved or reconciled.

SynchronizationStrategy model

PropertyTypeDescription
strategyString ("none", "watermark", "checksum")none (default) validates every partition on every run. watermark and checksum enable incremental validation.
watermarkColumnStringRequired when strategy is watermark. AIM DMV compares MAX(column) per partition against the stored baseline, so the column must be monotonically increasing.
checksumExpressionStringOptional when strategy is checksum. A SQL aggregate that replaces the default per-partition hash, for example MAX(ORA_ROWSCN). Must not contain a semicolon.

Note

trackModifications and trackDeletions are not supported for Data Validation.

Prerequisites

  • The table must be partitioned. Set columnNamesToPartitionBy, or let AIM DMV infer it. Change detection works per partition, so an unpartitioned table has nothing to skip.
  • At least one prior full validation must have completed, to establish baseline metadata in PARTITION_METADATA.SYNCHRONIZATION_DATA. The first run with synchronization configured still validates everything.

What happens on each run

RunBehavior
First runFull pipeline: L1, partition discovery, then L2 and L3 at the levels you enabled. Baselines are stored per partition.
Later runsL1 is skipped and its column metadata is reused. A change-detection task per partition compares the current probe against the baseline. Unchanged partitions skip L2 and L3; changed partitions are validated at the currently enabled levels.

Unchanged partitions report Not validated rather than a pass. That’s expected on an incremental run, not a failure.

Warning

An incremental run needs the same L1, L2, and L3 toggles as the run that established the baseline. If you change which levels are enabled, run a full validation (strategy: none) once to re-establish baseline metadata before returning to incremental runs.

Change-detection probes read partition boundaries only. sourceWhereClause and targetWhereClause aren’t applied while detecting change; they’re applied when L2 and L3 run on a partition that was found to have changed.

Because change detection relies on the same hashing as migration checksums, review Changes a checksum may not detect before using strategy: checksum. Columns excluded from the hash won’t trigger re-validation of their partition.

Example

sourcePlatform: sqlserver
targetDatabase: MY_DB
defaultTableConfiguration:
  columnNamesToPartitionBy:
    - ID
  synchronization:
    strategy: checksum
tables:
  # Inherits the checksum strategy and the partition column.
  - fullyQualifiedName: MYDB.dbo.CUSTOMERS
  # Overrides both: partition on ORDER_ID, detect change by watermark.
  - fullyQualifiedName: MYDB.dbo.ORDERS
    columnNamesToPartitionBy:
      - ORDER_ID
    synchronization:
      strategy: watermark
      watermarkColumn: UPDATED_AT
  # Opts out: always validate in full, despite the global default.
  - fullyQualifiedName: MYDB.dbo.LOOKUP_CODES
    synchronization:
      strategy: none

Anti-locking and query modifiers

Anti-locking hints are off by default and work the same way as in migration: they apply to source queries only and are set with queryModifiers at the workflow root or per table. See Anti-locking and query modifiers for per-platform behavior and configuration.

The validation-specific consideration: WITH (NOLOCK) and similar read-uncommitted hints allow dirty reads that can produce false MISMATCH results. The impact is usually stronger at row-level validation (byte-exact hashing) than at metrics validation (aggregates compared within tolerance). Enable these hints only when source locking is actually blocking your workflow, ideally against a low-write window.

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.

ValueBehavior
"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:

PropertyTypeRequiredDescription
dataTypeStringExactly one of dataType, column, columnPatternApplies the rule to every column of this data type.
columnStringExactly one of dataType, column, columnPatternApplies the rule to one exact column name.
columnPatternStringExactly one of dataType, column, columnPatternApplies the rule to column names matching this regex.
sourceExpressionStringAt least one of sourceExpression, targetExpressionSQL template in the source dialect. Use the {{ col_name }} placeholder for the column reference.
targetExpressionStringAt least one of sourceExpression, targetExpressionSQL 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.

validationCustomNormalizationRules:
  - dataType: FLOAT
    sourceExpression: "TRIM(CAST({{ col_name }} AS VARCHAR(100)))"
    targetExpression: "TRIM(TO_VARCHAR({{ col_name }}))"
  - column: AMT
    sourceExpression: "TRIM(CAST({{ col_name }} AS VARCHAR(50)))"
  - columnPattern: "^GEO_"
    sourceExpression: "ST_AsText({{ col_name }})"
    targetExpression: "TO_VARCHAR({{ col_name }})"
tables:
  - fullyQualifiedName: MYDB.MYSCHEMA.MYTABLE
    validationCustomNormalizationRules:
      - column: LEGACY_FLAG
        sourceExpression: "TRIM(CAST({{ col_name }} AS VARCHAR(10)))"
        targetExpression: "TRIM(TO_VARCHAR({{ col_name }}))"

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.

PropertyTypeDescription
sourceArrayOverrides applied on the source platform side.
targetArrayOverrides 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.

validationCustomNormalizations:
  source:
    - BIGINT: "TRIM(TO_CHAR(\"{{ col_name }}\", '999990.000000000000000000'))"
    - DECIMAL: "TRIM(TO_CHAR(\"{{ col_name }}\", '999990.000000000000000000'))"
  target:
    - NUMBER: "TO_CHAR(\"{{ col_name }}\", 'FM999990.000000000000000000')"

Custom metrics (validationCustomMetrics)

L2 metrics are chosen by column data type. A DEFAULT set applies to types without a specific entry.

PropertyTypeDescription
sourceArrayMetric overrides on the source platform side.
targetArrayMetric overrides on the Snowflake target side.

Each array entry:

FieldTypeDescription
datatypeStringSource or target data type (uppercased).
metricsArrayMetric definitions for that type.
replaceAllBooleanWhen true, removes all built-in metrics for the type before applying the listed metrics (default false).

Each metric definition:

FieldTypeDescription
nameStringMetric name (for example count, sum, min, max, stddev).
metricQueryStringSQL aggregate using "{{ col_name }}". A null or empty value removes that metric from the built-in set.
metricReturnDatatypeStringData type used to normalize the metric result.
metricColumnModifierStringOptional overflow guard override for this metric.

Control which metrics run with these related settings:

SettingDefaultPurpose
metricsValidationtrueEnable or disable L2 entirely (global or per table).
excludeMetricsfalseSkip overflow-prone aggregates: avg, sum, stddev.
applyMetricColumnModifiertrueApply platform overflow guards to aggregate metrics.
comparisonConfiguration.tolerance0.001Relative threshold for numeric L2 metric comparison only.

Example:

validationCustomMetrics:
  source:
    - datatype: BIT
      replaceAll: false
      metrics:
        - name: sum
          metricQuery: "SUM(\"{{ col_name }}\")"
          metricReturnDatatype: NUMBER
        - name: stddev_pop
          metricQuery: "STDDEV_POP(\"{{ col_name }}\")"
          metricReturnDatatype: FLOAT
  target: []

L1 type mapping overrides (validationCustomTypes)

validationCustomTypes overrides how source data type names map for L1 schema comparison. This is distinct from normalization (L2/L3).

PropertyTypeDescription
sourceArrayList of single-key maps, each from an original source type name to the normalized type name.

Example:

validationCustomTypes:
  source:
    - "PERIOD(DATE)": "VARCHAR(40)"

Per-column L1 type overrides (validationCustomTypeRules)

Where validationCustomTypes remaps a type name everywhere it appears, validationCustomTypeRules overrides the expected target type for one column. Use it when a single column was deliberately migrated to a different type than the default mapping would produce.

PropertyTypeRequiredDescription
columnStringYesSource column name.
sourceTypeStringYesType on the source.
targetTypeStringYesType expected on the target.
validationCustomTypeRules:
  - column: GEO_COL
    sourceType: GEOGRAPHY
    targetType: VARIANT

A rule affects only the L1 DATA_TYPE criterion. The other L1 criteria (CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, and NUMERIC_SCALE) still report the genuine metadata difference, because a type rule doesn’t claim those match.

A type rule also doesn’t change how values compare. When the two sides hold the same information in different types, pair the rule with a custom normalization rule so L3 sees equal values.

Note

L3 row validation requires schema validation. The workflow is rejected if you set schemaValidation: false while relying on them.

Set these rules at the workflow root or on an individual table entry. Per-table rules win for matching columns.

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, external secret managers, and environment variables.

Validation objects such as the results stage and file format live in the validation metadata schema. When you override its name, the Worker’s [application].snowflake_schema_for_data_validation_metadata key must match the Orchestrator’s CUSTOM_SNOWFLAKE_SCHEMA_FOR_DATA_VALIDATION_METADATA. See Matching metadata locations between the Orchestrator and Workers.

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.