The database refuses a value that no value set states
Table of Contents
1. Summary
A value that no set states is refused by a trigger on the table that stores it,
not by the service that writes it. Four trigger functions cover the four ways a
value reaches a row, and all of them delegate the verdict to one function,
ores_reporting_value_in_set_fn. That function asks
ores_reporting_value_in_set_item_fn what one value may be, and splits a comma
separated list first when the set states cardinality = many. A fifth function,
ores_reporting_setting_value_verdict_fn, answers the same question in a form a
caller can read: whether the setting names a set at all, which set, and whether
the value is in it. The engine settings sign-off path asks that one before a
person is asked to sign.
The reason for the database rather than the services is that the writers are plural: the shell, a screen through the BFF, an import job and a publish function all reach the same table, and only a trigger sees all four.
The same guard states what is stored. A set names the form a configuration holds
for a value ORE writes several ways, and the guard replaces the value with that
form before the row exists, through ores_reporting_canonical_value_fn. A
document that says A360 and one that says ACT/360 therefore leave one row, and
the rendering never reaches a table at all.
2. The four guards
| Trigger function | Attached to | Resolves the set by |
|---|---|---|
ores_reporting_validate_parameter_value_fn |
ores_analytics_pricing_model_product_parameters_tbl |
(scope, name) on the setting link |
ores_refdata_validate_curve_values_fn |
each of the refdata curve configuration tables | the column's row in the element map, which names the set |
ores_refdata_validate_convention_terms_fn |
each of the convention and currency pair tables | the term's row in the element map, which names the set |
ores_reporting_validate_engine_settings_part_fn |
the fixings, building and execution part tables | Setup: and the part's ORE name |
Each is a security definer trigger, and each raises an error that names the
value, the setting and the set. The message is the interface: a person reading a
failed import sees which rule refused them.
Each also stores the canonical form of a value it accepts, so a rendering does not reach a row by any of the four paths. The verdict and the conversion are two questions, and the guard asks them in that order: whether the value is in the set, then which form of it a configuration holds.
The part guard is the one that does not spell its own join. It reads the columns
a group holds from ores_reporting_engine_settings_group_settings_tbl, so a
column the group does not state is left alone and the guard does not know which
table it is attached to
(projects/ores.sql/create/reporting/reporting_engine_settings_part_value_guard_create.sql:46).
3. Why the verdict is a definer
A tenant-scoped caller that read the tables itself would meet row-level security, see nothing, and read a rule that does not exist.
That sentence is from the function's own comment
(projects/ores.sql/create/reporting/reporting_ore_parameter_value_validate_fn_create.sql:170),
and it is the whole argument. The value sets are shared rows in the system
tenant, and the writer is tenant-scoped. A check that ran with the writer's
rights would meet row-level security, see no sets, find no rule and admit the
value. Running the read as the definer is not a privilege shortcut; it is what
makes the rule visible at all.
The verdict function exists in this shape for the same reason: the reporting core
suite proves it answers for a tenant caller who cannot see the sets
(projects/ores.sql/test/reporting_setting_value_verdict_test.sql, five
assertions covering a found setting, a value in its set, a value outside it, a
typed value and a setting with no set).
4. Before the write, not only after it
The engine settings proposal service refuses a bad value at the desk. It calls
the same verdict function
(projects/ores.reporting/service/src/service/engine_settings_proposal.cpp:171)
and throws before the approval request exists, because a proposal nobody could
apply should not be signed. The comment there states the design rule the whole
cluster rests on: no copy of the rule lives in the service to drift from the
store's.
The screen standard follows the same shape: a screen reads what the set states and draws it, rather than carrying a copy of the rule.
5. The three places a missing rule still passes
All are stated here rather than left for a reader to discover.
- A leaf the element map states
textfor is admitted, because text is a rule that admits every value. For all 155 of the 477 leaves that number is a decision an ORE source justifies, and three more take the rule the ORE schema states for them: the extractor reads at least one form for every leaf, and the ratchet ingenerate_ore_element_map.py(UNREAD_ALLOWED) holds that count at zero so it can fall but not grow unnoticed. - A parameter with no set is admitted by the product parameter guard: it returns
the row when the link is not found
(
…reporting_ore_parameter_value_validate_fn_create.sql:231). The engine settings guard does the opposite and refuses, because every engine setting a group states is required to name a set. The shell's completeness gate,ore_parameter_settings check(projects/ores.shell/reporting/src/app/commands/reporting/ore_parameter_settings_commands.cpp:353), is what catches the product case: it fails when a parameter in the contract has no set or a set has no values, and it refuses to judge at all when the contract is not published for the tenant. - A set code that names no set admits every value
(
…reporting_ore_parameter_value_validate_fn_create.sql:87), and the canonical function answers with the value itself for the same reason. Nothing catches a typo in a link'svalue_set_code; only a foreign key would, and it is still missing.
Until both are closed, "the rule is always enforced" is true for a parameter that
has a set and false for one whose set is missing or misspelt. A reader checking a
specific value should ask ores_reporting_setting_value_verdict_fn and read
found, not is_in_set alone.
6. See also
- The ORE XML infrastructure — the structure note that orders this cluster; this page is its last step, and the one that says who has the last word on a value.
- A value set is the only place an ORE parameter's rule is written — answers what the guards read: this page is the reading, and that page is the thing read.
- One map answers which ORE element a row is — contradicts any claim that all three maps are enforced: the guards use the curve columns and the convention terms, so the vocabularies table states a rule with no enforcer.
- The export writes the stored form, so a round trip is not byte-identical — completes this page: because nothing reaches a row without the verdict, the export cannot emit a document that ORE would refuse.
- PostgreSQL Architecture — the cluster that owns the row-level security and definer conventions this page depends on.