Merge branch 'carry-every-date-through-an-adjustment'

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Trowbridge 2026-09-21 09:49:04 -04:00
commit cb9f98f551
2 changed files with 41 additions and 10 deletions

View File

@ -408,6 +408,18 @@ Turning a region back into slices re-derives, per cell, the same filters Perspec
All three operations follow the same structure: insert a `pf.log` row in a CTE, then insert forecast rows referencing its id. `{{where_clause}}` is built from the slice; `{{exclude_clause}}` blocks `exclude_iters` rows. All three operations follow the same structure: insert a `pf.log` row in a CTE, then insert forecast rows referencing its id. `{{where_clause}}` is built from the slice; `{{exclude_clause}}` blocks `exclude_iters` rows.
**Every date column travels, not just the first.** `dataCols` is
`[...dims, ...dateCols, value, units]`. It used to be `dateCols[0]`, so on a
source with order, requested and ship dates, scale and recode wrote rows whose
`rdate` and `sdate_e` were **null** — and because the derived period dimensions
(`rseas`, `smon_e`) are dimensions and were copied with the rest, the labels
read correctly while the dates under them were gone. Both columns are
`role = 'date'` and therefore sliceable, so an adjustment scoped to a ship date
matched no adjustment row that had ever been written. Clone and the loads were
always right. The wider GROUP BY costs almost nothing on `osm_skinny`
(971,185 → 971,352 groups) because `ordnum`/`ordline` already make a row
near-unique.
- **Scale** — distributes `value_incr`/`units_incr` proportionally across rows in the slice using window functions - **Scale** — distributes `value_incr`/`units_incr` proportionally across rows in the slice using window functions
- **Recode** — inserts negative rows (zero out original) + positive rows with `{{set_clause}}` dimension overrides; both share the same logid - **Recode** — inserts negative rows (zero out original) + positive rows with `{{set_clause}}` dimension overrides; both share the same logid
- **Clone** — copies the slice with `{{set_clause}}` overrides and `{{scale_factor}}` multiplier; original untouched - **Clone** — copies the slice with `{{set_clause}}` overrides and `{{scale_factor}}` multiplier; original untouched
@ -539,6 +551,13 @@ column.
## Known issues / active work ## Known issues / active work
- **Rows written before 2026-09-21 have null `rdate`/`sdate_e`.** The generator
fix above changes what gets written from here on; it does not repair what is
there. On `fc_osm_skinny_29` that is ~270k scale and recode rows. A backfill
would have to re-derive each from the baseline rows at the same coordinate,
which is possible but is inference, not recovery — deleting and replaying
those log entries is the honest repair
- **Zero-row operations report success.** Scale refuses with "Nothing to - **Zero-row operations report success.** Scale refuses with "Nothing to
scale…" when its slice matches nothing; recode and clone commit an empty log scale…" when its slice matches nothing; recode and clone commit an empty log
entry and return `rows_affected: 0`. A recode of a rep whose rows are all entry and return `rows_affected: 0`. A recode of a rep whose rows are all

View File

@ -198,19 +198,31 @@ function generateSQL(source, colMeta) {
const valueCol = valueCols[0]; const valueCol = valueCols[0];
const unitsCol = unitsCols[0]; const unitsCol = unitsCols[0];
const dateCol = dateCols[0];
if (!valueCol) throw new Error('No value column defined in col_meta'); if (!valueCol) throw new Error('No value column defined in col_meta');
if (!dateCol) throw new Error('No date column defined in col_meta'); if (!dateCols.length) throw new Error('No date column defined in col_meta');
if (dims.length === 0) throw new Error('No dimension columns defined in col_meta'); if (dims.length === 0) throw new Error('No dimension columns defined in col_meta');
const srcTable = `"${source.schema}"."${source.tname}"`; const srcTable = `"${source.schema}"."${source.tname}"`;
const dataCols = [...dims, dateCol, valueCol, unitsCol].filter(Boolean); // Every date column, not just the primary one. Scale and recode used to
// carry dateCols[0] alone, so a source with order, requested and ship dates
// wrote adjustment rows whose rdate and sdate_e were *null* -- not stale,
// absent. Those columns are role 'date' and therefore sliceable, so a later
// adjustment scoped to a ship date matched no adjustment row ever written,
// and any reader that dated the forecast by ship date lost every
// adjustment silently. The derived period dimensions (rseas, smon_e) were
// fine throughout, being dimensions and copied with the rest -- which is
// what made it invisible: the labels were right and the dates underneath
// them were gone.
const dataCols = [...dims, ...dateCols, valueCol, unitsCol].filter(Boolean);
const effectiveValue = dataCols.includes(valueCol) ? valueCol : null; const effectiveValue = dataCols.includes(valueCol) ? valueCol : null;
const effectiveUnits = dataCols.includes(unitsCol) ? unitsCol : null; const effectiveUnits = dataCols.includes(unitsCol) ? unitsCol : null;
const insertCols = [...dataCols.map(q), 'pf_iter', 'pf_logid', 'pf_user', 'pf_created_at'].join(', '); const insertCols = [...dataCols.map(q), 'pf_iter', 'pf_logid', 'pf_user', 'pf_created_at'].join(', ');
const selectData = dataCols.map(q).join(', '); const selectData = dataCols.map(q).join(', ');
const dimsJoined = dims.map(q).join(', '); const dimsJoined = dims.map(q).join(', ');
// The date columns as a select list, in the same order dataCols lists them,
// so it lines up with insertCols positionally.
const datesJoined = dateCols.map(q).join(', ');
// Baseline and reference copy the source row wholesale, so they carry every // Baseline and reference copy the source row wholesale, so they carry every
// measure and every date — not just the primary one the operations act on. // measure and every date — not just the primary one the operations act on.
@ -410,7 +422,7 @@ SELECT count(*) AS rows_affected, (SELECT id FROM ilog) AS log_id FROM ins`.trim
// comes to the same figure the ungrouped window produced. // comes to the same figure the ungrouped window produced.
const baseSelectParts = [ const baseSelectParts = [
...dimsJoined ? [dimsJoined] : [], ...dimsJoined ? [dimsJoined] : [],
q(dateCol), datesJoined,
effectiveValue ? `sum(${q(effectiveValue)}) AS ${q(effectiveValue)}` : null, effectiveValue ? `sum(${q(effectiveValue)}) AS ${q(effectiveValue)}` : null,
effectiveUnits ? `sum(${q(effectiveUnits)}) AS ${q(effectiveUnits)}` : null, effectiveUnits ? `sum(${q(effectiveUnits)}) AS ${q(effectiveUnits)}` : null,
effectiveValue ? `sum(sum(${q(effectiveValue)})) OVER () AS total_value` : null, effectiveValue ? `sum(sum(${q(effectiveValue)})) OVER () AS total_value` : null,
@ -430,12 +442,12 @@ ilog AS (
WHERE {{where_clause}} WHERE {{where_clause}}
{{exclude_clause}} {{exclude_clause}}
GROUP BY GROUP BY
${groupCols([...dims, dateCol])} ${groupCols([...dims, ...dateCols])}
) )
,ins AS ( ,ins AS (
INSERT INTO {{fc_table}} (${insertCols}) INSERT INTO {{fc_table}} (${insertCols})
SELECT SELECT
${[dimsJoined, q(dateCol), ...(effectiveValue ? [vSel] : []), ...(effectiveUnits ? [uSel] : [])].join(',\n ')}, ${[dimsJoined, datesJoined, ...(effectiveValue ? [vSel] : []), ...(effectiveUnits ? [uSel] : [])].join(',\n ')},
'scale', (SELECT id FROM ilog), '{{pf_user}}', now() 'scale', (SELECT id FROM ilog), '{{pf_user}}', now()
FROM base FROM base
RETURNING * RETURNING *
@ -454,23 +466,23 @@ ilog AS (
,src AS ( ,src AS (
SELECT SELECT
${dimsJoined}, ${dimsJoined},
${q(dateCol)}${effectiveValue ? `,\n sum(${q(effectiveValue)}) AS ${q(effectiveValue)}` : ''}${effectiveUnits ? `,\n sum(${q(effectiveUnits)}) AS ${q(effectiveUnits)}` : ''} ${datesJoined}${effectiveValue ? `,\n sum(${q(effectiveValue)}) AS ${q(effectiveValue)}` : ''}${effectiveUnits ? `,\n sum(${q(effectiveUnits)}) AS ${q(effectiveUnits)}` : ''}
FROM {{fc_table}} FROM {{fc_table}}
WHERE {{where_clause}} WHERE {{where_clause}}
{{exclude_clause}} {{exclude_clause}}
GROUP BY GROUP BY
${groupCols([...dims, dateCol])} ${groupCols([...dims, ...dateCols])}
) )
,neg AS ( ,neg AS (
INSERT INTO {{fc_table}} (${insertCols}) INSERT INTO {{fc_table}} (${insertCols})
SELECT ${dimsJoined}, ${q(dateCol)}, ${effectiveValue ? `-${q(effectiveValue)}` : '0'}${effectiveUnits ? `, -${q(effectiveUnits)}` : ''}, SELECT ${dimsJoined}, ${datesJoined}, ${effectiveValue ? `-${q(effectiveValue)}` : '0'}${effectiveUnits ? `, -${q(effectiveUnits)}` : ''},
'recode', (SELECT id FROM ilog), '{{pf_user}}', now() 'recode', (SELECT id FROM ilog), '{{pf_user}}', now()
FROM src FROM src
RETURNING * RETURNING *
) )
,ins AS ( ,ins AS (
INSERT INTO {{fc_table}} (${insertCols}) INSERT INTO {{fc_table}} (${insertCols})
SELECT {{set_clause}}, ${q(dateCol)}, ${effectiveValue ? q(effectiveValue) : '0'}${effectiveUnits ? `, ${q(effectiveUnits)}` : ''}, SELECT {{set_clause}}, ${datesJoined}, ${effectiveValue ? q(effectiveValue) : '0'}${effectiveUnits ? `, ${q(effectiveUnits)}` : ''},
'recode', (SELECT id FROM ilog), '{{pf_user}}', now() 'recode', (SELECT id FROM ilog), '{{pf_user}}', now()
FROM src FROM src
RETURNING * RETURNING *