The case insert trigger writes the result time, then the existing open persist sends the minted title back. A before-update guard keeps that result unless the user chose a non-automatic name, and a new session reads the stored title into the sidebar.
151 lines
5.6 KiB
PL/PgSQL
151 lines
5.6 KiB
PL/PgSQL
-- BUG-1001: rectification titles carry the result, not a date.
|
||
-- One formula and one automatic-title predicate. The trigger and the backfill
|
||
-- both call them. Do not assign chat_sessions.updated_at (BUG-988 / BUG-704).
|
||
-- Open / GET / metadata PATCH still must not retitle (BUG-699): this trigger
|
||
-- listens only to accepted_time, confirmed_time, and candidate_range.
|
||
|
||
begin;
|
||
|
||
do $migration$
|
||
begin
|
||
if current_user <> 'schema_owner' then
|
||
raise exception 'rectification_session_title_result_requires_schema_owner'
|
||
using errcode = '42501';
|
||
end if;
|
||
end
|
||
$migration$;
|
||
|
||
create or replace function public.rectification_session_title(
|
||
p_accepted time,
|
||
p_confirmed time,
|
||
p_range jsonb
|
||
) returns text
|
||
language sql
|
||
immutable
|
||
parallel safe
|
||
set search_path = ''
|
||
as $$
|
||
select case
|
||
when p_confirmed is not null then
|
||
'生时校正 · ' || to_char(p_confirmed, 'HH24:MI')
|
||
when p_accepted is not null then
|
||
'生时校正 · ' || to_char(p_accepted, 'HH24:MI')
|
||
when public.agentic_rectification_is_clock(p_range ->> 'start_time')
|
||
and public.agentic_rectification_is_clock(p_range ->> 'end_time') then
|
||
'生时校正 · ' || (p_range ->> 'start_time') || '–' || (p_range ->> 'end_time')
|
||
else
|
||
'生时校正'
|
||
end;
|
||
$$;
|
||
|
||
-- The dash in the range alternative is U+2013 EN DASH, not a hyphen.
|
||
-- frontend/src/lib/session-title.ts DATED_ENTRY_TITLE must agree with this
|
||
-- predicate. The parity test reads this pattern from this file.
|
||
create or replace function public.rectification_session_title_is_automatic(
|
||
p_title text
|
||
) returns boolean
|
||
language sql
|
||
immutable
|
||
parallel safe
|
||
set search_path = ''
|
||
as $$
|
||
select p_title is not null
|
||
and p_title ~ '^(?:(?:生时校正|今日节奏)\s*·\s*[0-9]{1,2}月[0-9]{1,2}日|[0-9]{1,2}月[0-9]{1,2}日\s*·\s*(?:今日节奏|生时校正))(?:\s+[0-9]{2}:[0-9]{2})?$|^生时校正$|^生时校正\s*·\s*[0-9]{2}:[0-9]{2}$|^生时校正\s*·\s*[0-9]{2}:[0-9]{2}–[0-9]{2}:[0-9]{2}$';
|
||
$$;
|
||
|
||
create or replace function public.sync_rectification_session_title_from_case()
|
||
returns trigger
|
||
language plpgsql
|
||
security definer
|
||
set search_path = ''
|
||
as $$
|
||
begin
|
||
update public.chat_sessions
|
||
set title = public.rectification_session_title(new.accepted_time, new.confirmed_time, new.candidate_range)
|
||
where id = new.session_id
|
||
and user_id = new.user_id
|
||
and session_type = 'birth_time_rectification'
|
||
and title is distinct from public.rectification_session_title(new.accepted_time, new.confirmed_time, new.candidate_range)
|
||
and public.rectification_session_title_is_automatic(title);
|
||
return new;
|
||
end;
|
||
$$;
|
||
|
||
revoke all on function public.rectification_session_title(time, time, jsonb)
|
||
from public, anon, authenticated;
|
||
revoke all on function public.rectification_session_title_is_automatic(text)
|
||
from public, anon, authenticated;
|
||
revoke all on function public.sync_rectification_session_title_from_case()
|
||
from public, anon, authenticated;
|
||
|
||
grant execute on function public.rectification_session_title(time, time, jsonb)
|
||
to schema_owner;
|
||
grant execute on function public.rectification_session_title_is_automatic(text)
|
||
to schema_owner, authenticated, service_role;
|
||
grant execute on function public.sync_rectification_session_title_from_case()
|
||
to schema_owner;
|
||
|
||
drop trigger if exists agentic_rectification_cases_title_from_result
|
||
on public.agentic_rectification_cases;
|
||
|
||
create trigger agentic_rectification_cases_title_from_result
|
||
after insert or update of accepted_time, confirmed_time, candidate_range
|
||
on public.agentic_rectification_cases
|
||
for each row
|
||
execute function public.sync_rectification_session_title_from_case();
|
||
|
||
-- Client PATCH runs as authenticated and would otherwise write the minted
|
||
-- title "生时校正" back over the result the insert trigger just stored.
|
||
-- This function stays security invoker so current_user is the updating role.
|
||
-- The result trigger is security definer, owned by schema_owner, so its
|
||
-- update still sees current_user = schema_owner and is allowed.
|
||
create or replace function public.keep_rectification_result_title()
|
||
returns trigger
|
||
language plpgsql
|
||
set search_path = ''
|
||
as $$
|
||
begin
|
||
if current_user <> 'schema_owner'
|
||
and new.session_type = 'birth_time_rectification'
|
||
and public.rectification_session_title_is_automatic(new.title)
|
||
and new.title is distinct from old.title then
|
||
new.title := old.title;
|
||
end if;
|
||
return new;
|
||
end;
|
||
$$;
|
||
|
||
revoke all on function public.keep_rectification_result_title()
|
||
from public, anon, authenticated;
|
||
grant execute on function public.keep_rectification_result_title()
|
||
to schema_owner;
|
||
|
||
drop trigger if exists chat_sessions_keep_rectification_result_title
|
||
on public.chat_sessions;
|
||
|
||
create trigger chat_sessions_keep_rectification_result_title
|
||
before update of title on public.chat_sessions
|
||
for each row
|
||
execute function public.keep_rectification_result_title();
|
||
|
||
do $backfill$
|
||
declare
|
||
retitled integer;
|
||
begin
|
||
-- BEGIN rectification_session_title_backfill
|
||
update public.chat_sessions as session
|
||
set title = public.rectification_session_title(case_row.accepted_time, case_row.confirmed_time, case_row.candidate_range)
|
||
from public.agentic_rectification_cases as case_row
|
||
where case_row.session_id = session.id
|
||
and case_row.user_id = session.user_id
|
||
and session.session_type = 'birth_time_rectification'
|
||
and session.title is distinct from public.rectification_session_title(case_row.accepted_time, case_row.confirmed_time, case_row.candidate_range)
|
||
and public.rectification_session_title_is_automatic(session.title);
|
||
-- END rectification_session_title_backfill
|
||
get diagnostics retitled = row_count;
|
||
raise notice 'rectification_session_title_result retitled=%', retitled;
|
||
end
|
||
$backfill$;
|
||
|
||
commit;
|