Schema: - records.overrides JSONB column (ALTER TABLE, already applied) - apply_transformations merges overrides on top: data || rules || overrides - generate_source_view always includes id and _overridden columns - set_record_overrides(id, overrides): stores and immediately merges into transformed - clear_record_overrides(id): clears overrides then reprocesses record API: - PUT /records/:id/overrides — set overrides - DELETE /records/:id/overrides — clear and reprocess UI (Records page): - Rows are clickable; overridden rows highlighted amber - Side panel shows all transformed fields as editable inputs - Overridden fields highlighted amber with pencil indicator - Save stores overrides; Clear removes them and restores computed values - id and _overridden hidden from table display Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
77 lines
2.8 KiB
PL/PgSQL
77 lines
2.8 KiB
PL/PgSQL
--
|
|
-- Records queries
|
|
-- All SQL for api/routes/records.js
|
|
--
|
|
|
|
SET search_path TO dataflow, public;
|
|
|
|
-- ── Read ──────────────────────────────────────────────────────────────────────
|
|
|
|
CREATE OR REPLACE FUNCTION list_records(
|
|
p_source_name TEXT,
|
|
p_limit INT DEFAULT 100,
|
|
p_offset INT DEFAULT 0,
|
|
p_transformed_only BOOLEAN DEFAULT FALSE
|
|
)
|
|
RETURNS SETOF dataflow.records AS $$
|
|
SELECT * FROM dataflow.records
|
|
WHERE source_name = p_source_name
|
|
AND (NOT p_transformed_only OR transformed IS NOT NULL)
|
|
ORDER BY id DESC
|
|
LIMIT p_limit OFFSET p_offset;
|
|
$$ LANGUAGE sql STABLE;
|
|
|
|
CREATE OR REPLACE FUNCTION get_record(p_id BIGINT)
|
|
RETURNS dataflow.records AS $$
|
|
SELECT * FROM dataflow.records WHERE id = p_id;
|
|
$$ LANGUAGE sql STABLE;
|
|
|
|
CREATE OR REPLACE FUNCTION search_records(
|
|
p_source_name TEXT,
|
|
p_query JSONB,
|
|
p_limit INT DEFAULT 100
|
|
)
|
|
RETURNS SETOF dataflow.records AS $$
|
|
SELECT * FROM dataflow.records
|
|
WHERE source_name = p_source_name
|
|
AND (data @> p_query OR transformed @> p_query)
|
|
ORDER BY id DESC
|
|
LIMIT p_limit;
|
|
$$ LANGUAGE sql STABLE;
|
|
|
|
-- ── Overrides ─────────────────────────────────────────────────────────────────
|
|
|
|
-- Store manual overrides and immediately merge into transformed
|
|
CREATE OR REPLACE FUNCTION set_record_overrides(p_id INT, p_overrides JSONB)
|
|
RETURNS dataflow.records AS $$
|
|
UPDATE dataflow.records
|
|
SET overrides = CASE WHEN p_overrides = '{}'::jsonb THEN NULL ELSE p_overrides END,
|
|
transformed = COALESCE(transformed, data) || COALESCE(p_overrides, '{}'::jsonb)
|
|
WHERE id = p_id
|
|
RETURNING *;
|
|
$$ LANGUAGE sql;
|
|
|
|
-- Clear overrides; caller should reprocess to restore computed transformed value
|
|
CREATE OR REPLACE FUNCTION clear_record_overrides(p_id INT)
|
|
RETURNS dataflow.records AS $$
|
|
UPDATE dataflow.records
|
|
SET overrides = NULL
|
|
WHERE id = p_id
|
|
RETURNING *;
|
|
$$ LANGUAGE sql;
|
|
|
|
-- ── Delete ────────────────────────────────────────────────────────────────────
|
|
|
|
CREATE OR REPLACE FUNCTION delete_record(p_id BIGINT)
|
|
RETURNS TABLE (id BIGINT) AS $$
|
|
DELETE FROM dataflow.records WHERE id = p_id RETURNING id;
|
|
$$ LANGUAGE sql;
|
|
|
|
CREATE OR REPLACE FUNCTION delete_source_records(p_source_name TEXT)
|
|
RETURNS TABLE (deleted_count BIGINT) AS $$
|
|
WITH deleted AS (
|
|
DELETE FROM dataflow.records WHERE source_name = p_source_name RETURNING id
|
|
)
|
|
SELECT count(*) AS deleted_count FROM deleted;
|
|
$$ LANGUAGE sql;
|