database/functions.sql held a full pg_dump appended onto the original hand-written file, duplicating ~50 functions that had since been split into database/queries/. Nothing deployed it, but CLAUDE.md and the tutorial still told you to psql it, which would have reverted the split versions. The reverse had also happened: five functions in queries/ were behind the live database, all of them undoing the May 2026 split of the transformed column. preview_rule lost its data -> transformed fallback for chained rules; set_/clear_/bulk_set_record_overrides wrote overrides back into transformed and returned the wrong type (which would have made the redeploy error outright); generate_source_view read only transformed instead of merging all three layers. Those are corrected here from the live definitions. generate_source_view additionally regains the _overridden column that queries/ had and live lacked — Records.jsx reads row._overridden to highlight manually edited rows, so that indicator had been dead. The seven functions that existed only in functions.sql move to two new files, import.sql (import + audit trail) and transform.sql (the rule/mapping engine), leaving database/ flat: schema.sql plus one file per route. The four already applied migrate_*.sql scripts are removed. manage.py picks up the new files in QUERY_FILES, and its DB_ACTIONS set now keys off the action functions rather than duplicated label strings that no longer matched any menu entry, so the "into database X" hint renders again. uninstall.sh is folded into manage.py as menu option 10. Beyond what the script did, it stops/disables/removes the systemd unit, removes the nginx site with an nginx -t check before reloading, and deletes public/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
3.7 KiB
PL/PgSQL
99 lines
3.7 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. Overrides stay in their own column — never merged into
|
|
-- transformed — so reprocessing rules cannot clobber a manual edit.
|
|
DROP FUNCTION IF EXISTS set_record_overrides(INTEGER, JSONB);
|
|
CREATE OR REPLACE FUNCTION set_record_overrides(p_id INTEGER, p_overrides JSONB)
|
|
RETURNS JSON AS $$
|
|
WITH updated AS (
|
|
UPDATE dataflow.records
|
|
SET overrides = CASE WHEN p_overrides = '{}'::jsonb THEN NULL ELSE p_overrides END
|
|
WHERE id = p_id
|
|
RETURNING *
|
|
)
|
|
SELECT row_to_json(updated) FROM updated;
|
|
$$ LANGUAGE sql;
|
|
|
|
-- Merge overrides into multiple records at once; returns actual updated count
|
|
DROP FUNCTION IF EXISTS bulk_set_record_overrides(TEXT, INTEGER[], JSONB);
|
|
CREATE OR REPLACE FUNCTION bulk_set_record_overrides(p_source_name TEXT, p_ids INTEGER[], p_overrides JSONB)
|
|
RETURNS JSON AS $$
|
|
WITH updated AS (
|
|
UPDATE dataflow.records
|
|
SET overrides = COALESCE(overrides, '{}'::jsonb) || p_overrides
|
|
WHERE id = ANY(p_ids)
|
|
AND source_name = p_source_name
|
|
RETURNING id
|
|
)
|
|
SELECT json_build_object('updated', count(*)) FROM updated;
|
|
$$ LANGUAGE sql;
|
|
|
|
-- Clear overrides; the computed values in transformed are untouched
|
|
DROP FUNCTION IF EXISTS clear_record_overrides(INTEGER);
|
|
CREATE OR REPLACE FUNCTION clear_record_overrides(p_id INTEGER)
|
|
RETURNS JSON AS $$
|
|
WITH updated AS (
|
|
UPDATE dataflow.records
|
|
SET overrides = NULL
|
|
WHERE id = p_id
|
|
RETURNING *
|
|
)
|
|
SELECT row_to_json(updated) FROM updated;
|
|
$$ 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;
|