Skip to content

fix(core-spec, ontology): reject a list that names the same thing twice - #705

Open
ayushtkn wants to merge 1 commit into
apache:mainfrom
ayushtkn:schema-unique-items
Open

ayushtkn wants to merge 1 commit into
apache:mainfrom
ayushtkn:schema-unique-items

Conversation

@ayushtkn

@ayushtkn ayushtkn commented Nov 7, 2026

Copy link
Copy Markdown
Member

Summary

No schema used uniqueItems, so a key could name one field twice and a list could carry the same entry twice:


  primary_key: [order_id, order_id]
  unique_keys: [[email], [email]]
  verbalizes: ["{Order} has {Total}", "{Order} has {Total}"]

A key that repeats a field declares fewer columns than it appears to, and a repeated entry constrains, derives, names or verbalizes nothing the first already did.

ossie-schema.json marks primary_key, unique_keys and each definition inside unique_keys. ontology.json marks the eight lists of names and expressions: extends, identify_by and verbalizes, and requires and derived_by wherever they appear.

models.py mirrors the two core-spec keys. Pydantic 2 dropped unique_items, so an AfterValidator stands in for it, as min_length already stands in for minItems.

uniqueItems compares entries as written, while a key means the set of fields its entries name, and regular identifiers are case insensitive (expression_language.md), so these still reached the semantic checks:

primary_key: [order_id, ORDER_ID]
unique_keys: [[order_id, line_number], [line_number, order_id]]

validate_key_declarations reports them. A key that names one field twice claims a column it does not have, which is an error. Two unique_keys definitions holding the same set of fields are one key written twice, which is redundant rather than wrong, so that is a warning.

An entry whose folded form names no declared field is left out of both: it names nothing to compare, validate_references reports it as undeclared, and calling it a repeat would name a field the dataset does not have. An empty entry, which passes the schema because key items carry no minLength, is dropped the same way but without discarding the entries beside it.

A dataset that declares two field names differing only in case declares one field twice, so a key naming both spellings names that field twice and is reported. validate_unique_names compares field names as written and does not report the declaration itself; closing that gap is a change of its own, and it does not make the key's two entries two fields.

A unique key that restates the primary key stays silent: DDL commonly declares both, and examples/tpcds_semantic_model.yaml does so in five of its datasets. Relationship from_columns and to_columns are left alone as well: they correspond positionally, so one column of the from dataset may legitimately be compared with two columns of the to dataset.

Related Issues

Checklist

Specification

  • Spec changes are included in core-spec/ and follow the existing structure
  • Spec changes have been discussed on the mailing list or in a linked issue
  • Breaking changes to the spec are clearly called out in the summary

Ontology

  • Ontology changes in ontology/ are consistent with spec changes
  • New or modified terms are defined and documented

Converters

Validation

  • Validation rules in validation/ are updated if the spec changed
  • New validation cases are covered by tests

Documentation

  • docs/ is updated to reflect any user-facing changes
  • New features or behaviors are documented with examples where appropriate
  • CONTRIBUTING.md is updated if the contribution process changed

Examples

  • examples/ are added or updated for any new spec constructs

Tests

  • All existing tests pass (pytest / CI green)
  • New functionality is covered by tests

Compliance

  • ASF license headers are present on all new source files
  • No third-party dependencies are added without PMC/IPMC approval
  • If third-party source code is vendored/copied (not just declared as a dependency), NOTICE and/or LICENSE have been updated per ASF policy

Copilot AI lite review requested due to automatic review settings November 7, 2026 03:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues remain, and the schema, validation, model, and regression-test changes collectively address duplicate-entry and uniqueness handling.

No open issues 🎉

What changed in this PR

This pull request prevents duplicate entries in core-spec keys and ontology lists, adds semantic validation for case-insensitive and reordered key duplicates, and keeps the Python models aligned with the updated schemas.

Changes:

  • Adds uniqueItems constraints to core-spec and ontology schemas.
  • Validates repeated key fields and redundant unique-key definitions.
  • Adds Pydantic validators mirroring schema uniqueness rules.
  • Expands regression coverage for schema, model, key, and ontology validation.

This pull request is too complex or risky for automated approval.

File Description
validation/​validate.py Adds case-insensitive key-declaration checks and redundancy warnings.
validation/​tests/​test_validate.py Tests duplicate, case-variant, undeclared, and redundant key scenarios.
validation/​tests/​test_ontology.py Tests duplicate detection across ontology lists.
python/​tests/​test_models.py Verifies model/schema uniqueness parity, including nested unique keys.
python/​src/​ossie/​models.py Adds Pydantic validators mirroring schema uniqueness constraints.
ontology/​ontology.json Rejects repeated entries across ontology expression and naming lists.
core-spec/​ossie-schema.json Rejects repeated primary-key fields and duplicate unique-key definitions.

Review effort: Lite

kayemkim
kayemkim previously approved these changes Nov 8, 2026

@kayemkim kayemkim left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the tree merged with current main: 37 checks pass on the four Python versions, and validate.py agrees with main on the 78 model and ontology documents I could find, converter fixtures included, so nothing shipped today repeats an entry. The folded cases behave as described: [order_id, ORDER_ID] is an error, the reordered unique_keys pair is a warning with exit 0, and a unique key restating the primary key stays silent. It also merges cleanly over #697.

Not blocking: this is a tightening, so a document with a repeated key entry that validated yesterday fails at the schema step today, which is a line for the migration notes #686 is collecting, and spec.md's description of primary_key could say the fields are distinct.

@jbonofre
jbonofre self-requested a review November 9, 2026 17:41
Comment thread core-spec/ossie-schema.json Outdated
"uniqueItems": true
},
"minItems": 1,
"uniqueItems": true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This outer uniqueItems makes unique_keys: [[email], [email]] a schema error. The validator stops on schema errors ("Semantic checks skipped until schema errors are fixed."), so the author also loses every reference and SQL diagnostic until it's fixed.

Meanwhile [[email], [EMAIL]] and [[id, email], [email, id]] are the same redundancy, and they only get the warning from validate_key_declarations, so those document pass. That function docstring, and the comment on OssieDataset.unique_keys, both call a repeated definition redundant rather than wrong. That points to a warning in every case.

Could we drop this outer uniqueKeys, and the outer AfterValidator(_distinct) on unique_keys in models.py, and let validate_key_declarations warn on exact repeats too?

If so, the repeat check there should compare definitions by position rather than with earlier is not key. Otherwise a YAML alias (unique_keys: [&k [email], *k]) passes the same list twice and isn't reported. The inner uniqueItems on each key and the one on primary_key are fine as errors: a key that names a field twice really is wrong.

"type": "string"
},
"minItems": 1,
"uniqueItems": true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new constraints should also go into core-spec/spec.md. The dataset table rows for primary_key and unique_keys don't say a field can't be named twice, and Version History has no entry. Documents that passed before this PR now fail, so the spec should say why.

For example, "Dataset fields that uniquely identify rows, each named once" for primary_key, the same rule for each unique_keys definition, and a Version History line for the change.

Comment thread ontology/ontology.json
"type": "string",
"minLength": 1
},
"uniqueItems": true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for the ontology. Since #673, ontology.md states each schema constraint in its field tables ("each must be non-empty"). The rows for extends, derived_by, identify_by, requires and verbalizes don't yet say that entries must be distinct.

Could we add that to those rows (e.g. "each must be non-empty and listed once") and add a Version History entry? Otherwise we bring back the schema/spec drift that #673 just fixed.

No schema used uniqueItems, so a key could name one field twice and a list
could carry the same entry twice:

  primary_key: [order_id, order_id]
  unique_keys: [[order_id, order_id]]
  verbalizes: ["{Order} has {Total}", "{Order} has {Total}"]

A key that repeats a field declares fewer columns than it appears to, and a
repeated entry constrains, derives, names or verbalizes nothing the first
already did.

ossie-schema.json marks primary_key and each definition inside unique_keys.
ontology.json marks the eight lists of names and expressions: extends,
identify_by and verbalizes, and requires and derived_by wherever they appear.

models.py mirrors the two core-spec keys. Pydantic 2 dropped unique_items, so
an AfterValidator stands in for it, as min_length already stands in for
minItems.

The list of unique_keys definitions carries no uniqueItems. A definition
written twice is redundant rather than wrong, and a schema error stops the
semantic checks, so the author would lose every reference and SQL diagnostic
over a document that states nothing untrue.

uniqueItems also compares entries as written, while a key means the set of
fields its entries name, and regular identifiers are case insensitive
(expression_language.md), so a schema keyword could not reach these at all:

  primary_key: [order_id, ORDER_ID]
  unique_keys: [[order_id, line_number], [line_number, order_id]]

validate_key_declarations reports both, and the verbatim repeat with them. A
key that names one field twice claims a column it does not have, which is an
error. Two unique_keys definitions holding the same set of fields are one key
written twice, which is redundant, so that is a warning. Definitions are
compared by position rather than by identity, because a YAML alias,
unique_keys: [&k [email], *k], makes one list object the value of two.

An entry whose folded form names no declared field is left out of both: it
names nothing to compare, validate_references reports it as undeclared, and
calling it a repeat would name a field the dataset does not have. An empty
entry, which passes the schema because key items carry no minLength, is
dropped the same way but without discarding the entries beside it.

A dataset that declares two field names differing only in case declares one
field twice, so a key naming both spellings names that field twice and is
reported. validate_unique_names compares field names as written and does not
report the declaration itself; closing that gap is a change of its own, and it
does not make the key's two entries two fields.

A unique key that restates the primary key stays silent: DDL commonly declares
both, and examples/tpcds_semantic_model.yaml does so in five of its datasets.
Relationship from_columns and to_columns are left alone as well: they
correspond positionally, so one column of the from dataset may legitimately be
compared with two columns of the to dataset.

The field tables state the constraints alongside the others they already
state. spec.md describes primary_key and unique_keys as naming each field
once, with a paragraph on the recased and reordered cases; ontology.md marks
its seven documented lists in the convention apache#673 introduced and documents the
root requires, which no row covered. Both Version Histories record the change,
and docs/spec_changes.md records its migration impact.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants