diff --git a/lib/sql_generator.js b/lib/sql_generator.js index bae5a24..efc9b96 100644 --- a/lib/sql_generator.js +++ b/lib/sql_generator.js @@ -8,7 +8,7 @@ // Tokens baked in at generation time: column names, source schema.table // Tokens substituted at request time: {{fc_table}}, {{where_clause}}, {{exclude_clause}}, // {{version_id}}, {{logid}}, {{pf_user}}, {{note}}, -// {{label}}, {{bucket}}, +// {{label}}, {{bucket}}, {{tag}}, // {{params}}, {{slice}}, {{date_from}}, {{date_to}}, // {{value_incr}}, {{units_incr}}, {{set_clause}}, {{scale_factor}} @@ -303,9 +303,9 @@ GROUP BY return ` WITH ilog AS ( - INSERT INTO pf.log (version_id, pf_user, operation, slice, params, note, label, bucket) + INSERT INTO pf.log (version_id, pf_user, operation, slice, params, note, label, bucket, tag) VALUES ({{version_id}}, '{{pf_user}}', 'baseline', NULL, '{{params}}'::jsonb, '{{note}}', - NULLIF('{{label}}', ''), NULLIF('{{bucket}}', '')) + NULLIF('{{label}}', ''), NULLIF('{{bucket}}', ''), NULLIF('{{tag}}', '')) RETURNING id ) ,ins AS ( @@ -324,9 +324,9 @@ SELECT count(*) AS rows_affected FROM ins`.trim(); return ` WITH ilog AS ( - INSERT INTO pf.log (version_id, pf_user, operation, slice, params, note, label, bucket) + INSERT INTO pf.log (version_id, pf_user, operation, slice, params, note, label, bucket, tag) VALUES ({{version_id}}, '{{pf_user}}', 'reference', NULL, '{{params}}'::jsonb, '{{note}}', - NULLIF('{{label}}', ''), NULLIF('{{bucket}}', '')) + NULLIF('{{label}}', ''), NULLIF('{{bucket}}', ''), NULLIF('{{tag}}', '')) RETURNING id ) ,ins AS ( diff --git a/routes/operations.js b/routes/operations.js index 3e6bfc7..47f11f0 100644 --- a/routes/operations.js +++ b/routes/operations.js @@ -409,7 +409,7 @@ module.exports = function(pool) { // load baseline rows from source table — additive, no delete router.post('/versions/:id/baseline', async (req, res) => { - const { where_clause, date_offset, note, filters, raw_where, label, bucket } = req.body; + const { where_clause, date_offset, note, filters, raw_where, label, bucket, tag } = req.body; const pf_user = sessionUser(req); const dateOffset = date_offset || '0 days'; if (!await assertInterval(dateOffset, res)) return; @@ -429,6 +429,7 @@ module.exports = function(pool) { note: esc(note || ''), label: esc(label || ''), bucket: esc(bucket || ''), + tag: esc(tag || ''), params: esc(paramsJson), filter_clause: filterClause, date_offset: esc(dateOffset) @@ -448,7 +449,7 @@ module.exports = function(pool) { router.put('/versions/:id/baseline/:logid', async (req, res) => { const versionId = parseInt(req.params.id); const logid = parseInt(req.params.logid); - const { where_clause, date_offset, note, filters, raw_where, label, bucket } = req.body; + const { where_clause, date_offset, note, filters, raw_where, label, bucket, tag } = req.body; const pf_user = sessionUser(req); const dateOffset = date_offset || '0 days'; if (!await assertInterval(dateOffset, res)) return; @@ -487,13 +488,21 @@ module.exports = function(pool) { date_offset: dateOffset, ...(raw_where ? { raw_where } : (filters ? { filters } : {})) }); + // This route deletes the log row and inserts a fresh one, so every + // annotation on it has to be handed back or it is lost. `??`, not `||`: + // an empty string is the form clearing a field on purpose, undefined is + // the form not carrying it at all -- the segment form has no tag input, + // so tag is always the latter and must survive an edit made for any + // other reason. + const keep = (sent, prior) => esc(sent ?? prior ?? ''); const sql = applyTokens(ctx.sql, { fc_table: ctx.table, version_id: ctx.version.id, pf_user: esc(pf_user || ''), note: esc(note || ''), - label: esc(label || ''), - bucket: esc(bucket || ''), + label: keep(label, oldLog.label), + bucket: keep(bucket, oldLog.bucket), + tag: keep(tag, oldLog.tag), params: esc(paramsJson), filter_clause: filterClause, date_offset: esc(dateOffset) @@ -558,7 +567,7 @@ module.exports = function(pool) { // load reference rows from source table (additive — does not clear prior reference rows) router.post('/versions/:id/reference', async (req, res) => { - const { where_clause, date_offset, note, filters, raw_where, label, bucket } = req.body; + const { where_clause, date_offset, note, filters, raw_where, label, bucket, tag } = req.body; const pf_user = sessionUser(req); const dateOffset = date_offset || '0 days'; const filterClause = (raw_where || where_clause || '').trim() || 'TRUE'; @@ -577,6 +586,7 @@ module.exports = function(pool) { note: esc(note || ''), label: esc(label || ''), bucket: esc(bucket || ''), + tag: esc(tag || ''), params: esc(paramsJson), filter_clause: filterClause, date_offset: esc(dateOffset)