1373 lines
55 KiB
PL/PgSQL
1373 lines
55 KiB
PL/PgSQL
begin;
|
|
|
|
create or replace function public.conversational_rectification_has_only_keys(
|
|
p_value jsonb,
|
|
p_allowed text[]
|
|
)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.jsonb_typeof(p_value) = 'object'
|
|
and not exists (
|
|
select 1
|
|
from pg_catalog.jsonb_object_keys(p_value) key
|
|
where not (key = any (p_allowed))
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_time_text(p_value text)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select p_value ~ '^([01][0-9]|2[0-3]):[0-5][0-9]$';
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_date_text(p_value text)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
begin
|
|
return p_value ~ '^[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}$'
|
|
and pg_catalog.to_char(p_value::date, 'YYYY-MM-DD') = p_value;
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_uuid_text(p_value text)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
begin
|
|
if p_value !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' then
|
|
return false;
|
|
end if;
|
|
perform p_value::uuid;
|
|
return true;
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_text_utf16_length(
|
|
p_value text
|
|
)
|
|
returns integer
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.char_length(p_value) + (
|
|
select pg_catalog.count(*)::integer
|
|
from pg_catalog.generate_series(
|
|
1, pg_catalog.char_length(p_value)
|
|
) character_positions(character_index)
|
|
where pg_catalog.octet_length(
|
|
pg_catalog.substr(p_value, character_index, 1)
|
|
) = 4
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_text_is_nonblank(
|
|
p_value text
|
|
)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.btrim(
|
|
p_value,
|
|
U&'\0009\000A\000B\000C\000D\0020\00A0\1680\2000\2001\2002\2003\2004\2005\2006\2007\2008\2009\200A\2028\2029\202F\205F\3000\FEFF'
|
|
) <> '';
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_numbers_are_stable(
|
|
p_value jsonb
|
|
)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
declare
|
|
v_item jsonb;
|
|
v_number numeric;
|
|
begin
|
|
if pg_catalog.jsonb_typeof(p_value) = 'number' then
|
|
v_number := (p_value #>> '{}')::numeric;
|
|
if v_number = pg_catalog.trunc(v_number) then
|
|
return pg_catalog.abs(v_number) <= 9007199254740991;
|
|
end if;
|
|
return pg_catalog.abs(v_number) between 0.000001 and 1000000
|
|
and v_number = pg_catalog.trunc(v_number, 6);
|
|
elsif pg_catalog.jsonb_typeof(p_value) = 'array' then
|
|
for v_item in select value from pg_catalog.jsonb_array_elements(p_value) loop
|
|
if public.conversational_rectification_numbers_are_stable(v_item) is not true then
|
|
return false;
|
|
end if;
|
|
end loop;
|
|
elsif pg_catalog.jsonb_typeof(p_value) = 'object' then
|
|
for v_item in select value from pg_catalog.jsonb_each(p_value) loop
|
|
if public.conversational_rectification_numbers_are_stable(v_item) is not true then
|
|
return false;
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
return true;
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_text_array_is_bounded(
|
|
p_value jsonb,
|
|
p_max_items integer,
|
|
p_max_characters integer,
|
|
p_max_bytes integer
|
|
)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.jsonb_typeof(p_value) = 'array'
|
|
and pg_catalog.jsonb_array_length(p_value) <= p_max_items
|
|
and pg_catalog.octet_length(p_value::text) <= p_max_bytes
|
|
and not exists (
|
|
select 1
|
|
from pg_catalog.jsonb_array_elements(p_value) item
|
|
where pg_catalog.jsonb_typeof(item) is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
item #>> '{}'
|
|
) not between 1 and p_max_characters
|
|
or public.conversational_rectification_text_is_nonblank(item #>> '{}') is not true
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_candidate(p_value jsonb)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
declare
|
|
v_key text;
|
|
begin
|
|
if pg_catalog.jsonb_typeof(p_value) is distinct from 'object'
|
|
or pg_catalog.octet_length(p_value::text) > 512
|
|
or public.conversational_rectification_numbers_are_stable(p_value) is not true
|
|
or not public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array['status', 'representativeTime', 'rangeStart', 'rangeEnd']::text[]
|
|
)
|
|
or not (p_value ?& array['status', 'representativeTime', 'rangeStart', 'rangeEnd']::text[])
|
|
or pg_catalog.jsonb_typeof(p_value -> 'status') is distinct from 'string'
|
|
or p_value ->> 'status' not in (
|
|
'declared', 'pending_validation', 'ready_for_confirmation', 'confirmed'
|
|
) then
|
|
return false;
|
|
end if;
|
|
foreach v_key in array array['representativeTime', 'rangeStart', 'rangeEnd']::text[] loop
|
|
if p_value -> v_key <> 'null'::jsonb
|
|
and (
|
|
pg_catalog.jsonb_typeof(p_value -> v_key) is distinct from 'string'
|
|
or not public.conversational_rectification_valid_time_text(p_value ->> v_key)
|
|
) then
|
|
return false;
|
|
end if;
|
|
end loop;
|
|
return true;
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_technical_receipt(
|
|
p_value jsonb
|
|
)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.jsonb_typeof(p_value) = 'object'
|
|
and pg_catalog.octet_length(p_value::text) <= 8192
|
|
and public.conversational_rectification_numbers_are_stable(p_value)
|
|
and public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array[
|
|
'calculationVersion', 'stableLayers', 'sensitiveLayers',
|
|
'candidateDifferenceRefs'
|
|
]::text[]
|
|
)
|
|
and p_value ?& array[
|
|
'calculationVersion', 'stableLayers', 'sensitiveLayers',
|
|
'candidateDifferenceRefs'
|
|
]::text[]
|
|
and pg_catalog.jsonb_typeof(p_value -> 'calculationVersion') = 'string'
|
|
and public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'calculationVersion'
|
|
) between 1 and 80
|
|
and public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'calculationVersion'
|
|
)
|
|
and public.conversational_rectification_text_array_is_bounded(
|
|
p_value -> 'stableLayers', 20, 80, 4096
|
|
)
|
|
and public.conversational_rectification_text_array_is_bounded(
|
|
p_value -> 'sensitiveLayers', 20, 80, 4096
|
|
)
|
|
and public.conversational_rectification_text_array_is_bounded(
|
|
p_value -> 'candidateDifferenceRefs', 40, 120, 8192
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_evidence_request(
|
|
p_value jsonb
|
|
)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.jsonb_typeof(p_value) = 'object'
|
|
and pg_catalog.octet_length(p_value::text) <= 2048
|
|
and public.conversational_rectification_numbers_are_stable(p_value)
|
|
and public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array['domains', 'datePrecision', 'freeTextAllowed']::text[]
|
|
)
|
|
and p_value ?& array['domains', 'datePrecision', 'freeTextAllowed']::text[]
|
|
and pg_catalog.jsonb_typeof(p_value -> 'domains') = 'array'
|
|
and pg_catalog.jsonb_array_length(p_value -> 'domains') between 2 and 4
|
|
and not exists (
|
|
select 1
|
|
from pg_catalog.jsonb_array_elements(p_value -> 'domains') domain
|
|
where pg_catalog.jsonb_typeof(domain) is distinct from 'string'
|
|
or domain #>> '{}' not in (
|
|
'career', 'education', 'relocation', 'relationship', 'family', 'other'
|
|
)
|
|
)
|
|
and pg_catalog.jsonb_typeof(p_value -> 'datePrecision') = 'string'
|
|
and p_value ->> 'datePrecision' in ('month_preferred', 'year_accepted')
|
|
and pg_catalog.jsonb_typeof(p_value -> 'freeTextAllowed') = 'boolean'
|
|
and p_value -> 'freeTextAllowed' = 'true'::jsonb;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_evidence_recap(
|
|
p_value jsonb
|
|
)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.jsonb_typeof(p_value) = 'array'
|
|
and pg_catalog.octet_length(p_value::text) <= 24576
|
|
and public.conversational_rectification_numbers_are_stable(p_value)
|
|
and pg_catalog.jsonb_array_length(p_value) <= 20
|
|
and not exists (
|
|
select 1
|
|
from pg_catalog.jsonb_array_elements(p_value) item
|
|
where pg_catalog.jsonb_typeof(item) <> 'object'
|
|
or pg_catalog.octet_length(item::text) > 4096
|
|
or not public.conversational_rectification_has_only_keys(
|
|
item, array['id', 'summary', 'dateLabel', 'isCorrection']::text[]
|
|
)
|
|
or not (item ?& array['id', 'summary', 'dateLabel']::text[])
|
|
or pg_catalog.jsonb_typeof(item -> 'id') <> 'string'
|
|
or not public.conversational_rectification_valid_uuid_text(item ->> 'id')
|
|
or pg_catalog.jsonb_typeof(item -> 'summary') <> 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
item ->> 'summary'
|
|
) not between 1 and 1000
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
item ->> 'summary'
|
|
) is not true
|
|
or pg_catalog.jsonb_typeof(item -> 'dateLabel') <> 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
item ->> 'dateLabel'
|
|
) not between 1 and 80
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
item ->> 'dateLabel'
|
|
) is not true
|
|
or (
|
|
item ? 'isCorrection'
|
|
and pg_catalog.jsonb_typeof(item -> 'isCorrection') <> 'boolean'
|
|
)
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_actions(p_value jsonb)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.jsonb_typeof(p_value) = 'array'
|
|
and pg_catalog.octet_length(p_value::text) <= 512
|
|
and public.conversational_rectification_numbers_are_stable(p_value)
|
|
and pg_catalog.jsonb_array_length(p_value) <= 5
|
|
and not exists (
|
|
select 1
|
|
from pg_catalog.jsonb_array_elements(p_value) action
|
|
where pg_catalog.jsonb_typeof(action) is distinct from 'string'
|
|
or action #>> '{}' not in (
|
|
'answer', 'pause', 'abandon', 'confirm', 'continue_original_question'
|
|
)
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_validation_receipt(
|
|
p_value jsonb
|
|
)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
begin
|
|
if pg_catalog.jsonb_typeof(p_value) is distinct from 'object'
|
|
or pg_catalog.octet_length(p_value::text) > 8192
|
|
or public.conversational_rectification_numbers_are_stable(p_value) is not true
|
|
or not public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array[
|
|
'modelId', 'schemaValidated', 'validatorVersion', 'validatedAt',
|
|
'retryCount', 'fallbackUsed', 'issues'
|
|
]::text[]
|
|
)
|
|
or not (p_value ?& array['modelId', 'schemaValidated']::text[])
|
|
or pg_catalog.jsonb_typeof(p_value -> 'modelId') is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'modelId'
|
|
) not between 1 and 120
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'modelId'
|
|
) is not true
|
|
or pg_catalog.jsonb_typeof(p_value -> 'schemaValidated') is distinct from 'boolean' then
|
|
return false;
|
|
end if;
|
|
if p_value ? 'validatorVersion' and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'validatorVersion') is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'validatorVersion'
|
|
) not between 1 and 80
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'validatorVersion'
|
|
) is not true
|
|
) then return false; end if;
|
|
if p_value ? 'validatedAt' and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'validatedAt') is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'validatedAt'
|
|
) not between 20 and 40
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'validatedAt'
|
|
) is not true
|
|
or p_value ->> 'validatedAt' !~
|
|
'^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$'
|
|
or (p_value ->> 'validatedAt')::timestamptz is null
|
|
) then return false; end if;
|
|
if p_value ? 'retryCount' and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'retryCount') is distinct from 'number'
|
|
or p_value ->> 'retryCount' !~ '^[0-9]+$'
|
|
or (p_value ->> 'retryCount')::integer not between 0 and 2
|
|
) then return false; end if;
|
|
if p_value ? 'fallbackUsed'
|
|
and pg_catalog.jsonb_typeof(p_value -> 'fallbackUsed') is distinct from 'boolean' then
|
|
return false;
|
|
end if;
|
|
if p_value ? 'issues' and not public.conversational_rectification_text_array_is_bounded(
|
|
p_value -> 'issues', 20, 240, 8192
|
|
) then return false; end if;
|
|
return true;
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_life_event_evidence(
|
|
p_value jsonb
|
|
)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
begin
|
|
if pg_catalog.jsonb_typeof(p_value) is distinct from 'object'
|
|
or pg_catalog.octet_length(p_value::text) > 16384
|
|
or public.conversational_rectification_numbers_are_stable(p_value) is not true
|
|
or not public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array[
|
|
'id', 'rawText', 'domain', 'eventSummary', 'dateValue',
|
|
'datePrecision', 'extractionStatus', 'scoreable', 'correctsEvidenceIds'
|
|
]::text[]
|
|
)
|
|
or not (p_value ?& array[
|
|
'id', 'rawText', 'domain', 'eventSummary', 'dateValue',
|
|
'datePrecision', 'extractionStatus'
|
|
]::text[])
|
|
or pg_catalog.jsonb_typeof(p_value -> 'id') is distinct from 'string'
|
|
or not public.conversational_rectification_valid_uuid_text(p_value ->> 'id')
|
|
or p_value ->> 'id' !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
|
|
or pg_catalog.jsonb_typeof(p_value -> 'rawText') is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'rawText'
|
|
) not between 1 and 4000
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'rawText'
|
|
) is not true
|
|
or pg_catalog.jsonb_typeof(p_value -> 'domain') is distinct from 'string'
|
|
or p_value ->> 'domain' not in (
|
|
'career', 'education', 'relocation', 'relationship', 'family', 'other'
|
|
)
|
|
or pg_catalog.jsonb_typeof(p_value -> 'eventSummary') is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'eventSummary'
|
|
) not between 1 and 1000
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'eventSummary'
|
|
) is not true
|
|
or not (p_value ? 'dateValue')
|
|
or (
|
|
p_value -> 'dateValue' <> 'null'::jsonb
|
|
and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'dateValue') is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'dateValue'
|
|
) not between 1 and 80
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'dateValue'
|
|
) is not true
|
|
)
|
|
)
|
|
or pg_catalog.jsonb_typeof(p_value -> 'datePrecision') is distinct from 'string'
|
|
or p_value ->> 'datePrecision' not in ('day', 'month', 'year', 'range', 'unknown')
|
|
or pg_catalog.jsonb_typeof(p_value -> 'extractionStatus') is distinct from 'string'
|
|
or p_value ->> 'extractionStatus' not in (
|
|
'clear', 'needs_clarification', 'corrected'
|
|
)
|
|
or (
|
|
p_value ? 'scoreable'
|
|
and pg_catalog.jsonb_typeof(p_value -> 'scoreable') is distinct from 'boolean'
|
|
)
|
|
or (
|
|
p_value ? 'correctsEvidenceIds'
|
|
and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'correctsEvidenceIds') is distinct from 'array'
|
|
or pg_catalog.octet_length((p_value -> 'correctsEvidenceIds')::text) > 64
|
|
or pg_catalog.jsonb_array_length(p_value -> 'correctsEvidenceIds') > 1
|
|
or exists (
|
|
select 1
|
|
from pg_catalog.jsonb_array_elements(p_value -> 'correctsEvidenceIds') target
|
|
where pg_catalog.jsonb_typeof(target) is distinct from 'string'
|
|
or public.conversational_rectification_valid_uuid_text(target #>> '{}') is not true
|
|
)
|
|
)
|
|
)
|
|
or (
|
|
p_value ->> 'extractionStatus' = 'corrected'
|
|
and (
|
|
not (p_value ? 'correctsEvidenceIds')
|
|
or pg_catalog.jsonb_array_length(p_value -> 'correctsEvidenceIds') <> 1
|
|
)
|
|
)
|
|
or (
|
|
p_value ->> 'extractionStatus' = 'clear'
|
|
and p_value ? 'correctsEvidenceIds'
|
|
and pg_catalog.jsonb_array_length(p_value -> 'correctsEvidenceIds') <> 0
|
|
) then
|
|
return false;
|
|
end if;
|
|
return true;
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_life_event_evidence_array(
|
|
p_value jsonb
|
|
)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
declare
|
|
v_item jsonb;
|
|
begin
|
|
if pg_catalog.jsonb_typeof(p_value) is distinct from 'array'
|
|
or pg_catalog.jsonb_array_length(p_value) > 20 then
|
|
return false;
|
|
end if;
|
|
for v_item in select value from pg_catalog.jsonb_array_elements(p_value) loop
|
|
if public.conversational_rectification_valid_life_event_evidence(v_item) is not true then
|
|
return false;
|
|
end if;
|
|
end loop;
|
|
return true;
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_private_candidate(
|
|
p_value jsonb
|
|
)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
declare
|
|
v_item jsonb;
|
|
v_key text;
|
|
begin
|
|
if pg_catalog.jsonb_typeof(p_value) is distinct from 'object'
|
|
or pg_catalog.octet_length(p_value::text) > 65536
|
|
or public.conversational_rectification_numbers_are_stable(p_value) is not true
|
|
or not public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array[
|
|
'resultId', 'representativeTime', 'rangeStart', 'rangeEnd',
|
|
'calculationVersion', 'candidateWeights', 'candidateModelRefs',
|
|
'd1Stability', 'boundaryDistanceMinutes', 'supportedSensitiveLayers',
|
|
'scoredHistoricalEvidence', 'suggestedDomains', 'futureWindows', 'workingState'
|
|
]::text[]
|
|
)
|
|
or not (p_value ? 'calculationVersion')
|
|
or pg_catalog.jsonb_typeof(p_value -> 'calculationVersion') is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'calculationVersion'
|
|
) not between 1 and 80
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'calculationVersion'
|
|
) is not true then
|
|
return false;
|
|
end if;
|
|
if p_value ? 'resultId' and p_value -> 'resultId' <> 'null'::jsonb and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'resultId') is distinct from 'string'
|
|
or not public.conversational_rectification_valid_uuid_text(p_value ->> 'resultId')
|
|
) then return false; end if;
|
|
foreach v_key in array array['representativeTime', 'rangeStart', 'rangeEnd']::text[] loop
|
|
if p_value ? v_key and p_value -> v_key <> 'null'::jsonb and (
|
|
pg_catalog.jsonb_typeof(p_value -> v_key) is distinct from 'string'
|
|
or not public.conversational_rectification_valid_time_text(p_value ->> v_key)
|
|
) then return false; end if;
|
|
end loop;
|
|
if (p_value ? 'rangeStart') <> (p_value ? 'rangeEnd')
|
|
or (p_value -> 'rangeStart' = 'null'::jsonb) <> (p_value -> 'rangeEnd' = 'null'::jsonb) then
|
|
return false;
|
|
end if;
|
|
if p_value ? 'candidateWeights' and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'candidateWeights') is distinct from 'array'
|
|
or pg_catalog.jsonb_array_length(p_value -> 'candidateWeights') > 1440
|
|
or exists (
|
|
select 1 from pg_catalog.jsonb_array_elements(p_value -> 'candidateWeights') weight
|
|
where pg_catalog.jsonb_typeof(weight) <> 'number'
|
|
or (weight #>> '{}')::numeric not between 0 and 1
|
|
)
|
|
) then return false; end if;
|
|
if p_value ? 'candidateModelRefs' and not public.conversational_rectification_text_array_is_bounded(
|
|
p_value -> 'candidateModelRefs', 80, 120, 16384
|
|
) then return false; end if;
|
|
if p_value ? 'supportedSensitiveLayers' and not public.conversational_rectification_text_array_is_bounded(
|
|
p_value -> 'supportedSensitiveLayers', 40, 80, 8192
|
|
) then return false; end if;
|
|
if p_value ? 'd1Stability' and p_value ->> 'd1Stability' not in (
|
|
'stable', 'sensitive', 'unavailable'
|
|
) then return false; end if;
|
|
if p_value ? 'd1Stability'
|
|
and pg_catalog.jsonb_typeof(p_value -> 'd1Stability') is distinct from 'string' then
|
|
return false;
|
|
end if;
|
|
if p_value ? 'boundaryDistanceMinutes' and p_value -> 'boundaryDistanceMinutes' <> 'null'::jsonb and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'boundaryDistanceMinutes') is distinct from 'number'
|
|
or p_value ->> 'boundaryDistanceMinutes' !~ '^[0-9]+$'
|
|
or (p_value ->> 'boundaryDistanceMinutes')::integer not between 0 and 1440
|
|
) then return false; end if;
|
|
if p_value ? 'suggestedDomains' and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'suggestedDomains') is distinct from 'array'
|
|
or pg_catalog.jsonb_array_length(p_value -> 'suggestedDomains') > 6
|
|
or exists (
|
|
select 1 from pg_catalog.jsonb_array_elements(p_value -> 'suggestedDomains') domain
|
|
where pg_catalog.jsonb_typeof(domain) is distinct from 'string'
|
|
or domain #>> '{}' not in (
|
|
'career', 'education', 'relocation', 'relationship', 'family', 'other'
|
|
)
|
|
)
|
|
) then return false; end if;
|
|
if p_value ? 'scoredHistoricalEvidence' then
|
|
if pg_catalog.jsonb_typeof(p_value -> 'scoredHistoricalEvidence') is distinct from 'array'
|
|
or pg_catalog.jsonb_array_length(p_value -> 'scoredHistoricalEvidence') > 100 then
|
|
return false;
|
|
end if;
|
|
for v_item in select value from pg_catalog.jsonb_array_elements(p_value -> 'scoredHistoricalEvidence') loop
|
|
if pg_catalog.jsonb_typeof(v_item) is distinct from 'object'
|
|
or pg_catalog.octet_length(v_item::text) > 8192
|
|
or not public.conversational_rectification_has_only_keys(
|
|
v_item, array['evidenceId', 'domain', 'candidateTime', 'score', 'ruleRefs']::text[]
|
|
)
|
|
or not (v_item ?& array['evidenceId', 'domain', 'candidateTime', 'score', 'ruleRefs']::text[])
|
|
or pg_catalog.jsonb_typeof(v_item -> 'evidenceId') is distinct from 'string'
|
|
or not public.conversational_rectification_valid_uuid_text(v_item ->> 'evidenceId')
|
|
or pg_catalog.jsonb_typeof(v_item -> 'domain') is distinct from 'string'
|
|
or v_item ->> 'domain' not in ('career', 'education', 'relocation', 'relationship', 'family', 'other')
|
|
or (v_item -> 'candidateTime' <> 'null'::jsonb and (
|
|
pg_catalog.jsonb_typeof(v_item -> 'candidateTime') is distinct from 'string'
|
|
or not public.conversational_rectification_valid_time_text(v_item ->> 'candidateTime')
|
|
))
|
|
or pg_catalog.jsonb_typeof(v_item -> 'score') is distinct from 'number'
|
|
or (v_item ->> 'score')::numeric not between -1000000 and 1000000
|
|
or not public.conversational_rectification_text_array_is_bounded(
|
|
v_item -> 'ruleRefs', 40, 120, 8192
|
|
) then return false; end if;
|
|
end loop;
|
|
end if;
|
|
if p_value ? 'futureWindows' then
|
|
if pg_catalog.jsonb_typeof(p_value -> 'futureWindows') is distinct from 'array'
|
|
or pg_catalog.jsonb_array_length(p_value -> 'futureWindows') > 20 then return false; end if;
|
|
for v_item in select value from pg_catalog.jsonb_array_elements(p_value -> 'futureWindows') loop
|
|
if pg_catalog.jsonb_typeof(v_item) is distinct from 'object'
|
|
or pg_catalog.octet_length(v_item::text) > 2048
|
|
or not public.conversational_rectification_has_only_keys(
|
|
v_item, array['label', 'startDate', 'endDate', 'scoreable']::text[]
|
|
)
|
|
or not (v_item ?& array['label', 'startDate', 'endDate', 'scoreable']::text[])
|
|
or pg_catalog.jsonb_typeof(v_item -> 'label') is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
v_item ->> 'label'
|
|
) not between 1 and 240
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
v_item ->> 'label'
|
|
) is not true
|
|
or pg_catalog.jsonb_typeof(v_item -> 'startDate') is distinct from 'string'
|
|
or pg_catalog.jsonb_typeof(v_item -> 'endDate') is distinct from 'string'
|
|
or not public.conversational_rectification_valid_date_text(v_item ->> 'startDate')
|
|
or not public.conversational_rectification_valid_date_text(v_item ->> 'endDate')
|
|
or v_item ->> 'startDate' > v_item ->> 'endDate'
|
|
or v_item -> 'scoreable' <> 'false'::jsonb then return false; end if;
|
|
end loop;
|
|
end if;
|
|
if p_value ? 'workingState' then
|
|
v_item := p_value -> 'workingState';
|
|
if pg_catalog.jsonb_typeof(v_item) is distinct from 'object'
|
|
or pg_catalog.octet_length(v_item::text) > 8192
|
|
or not public.conversational_rectification_has_only_keys(
|
|
v_item, array['phase', 'iteration', 'notes']::text[]
|
|
)
|
|
or not (v_item ?& array['phase', 'iteration', 'notes']::text[])
|
|
or pg_catalog.jsonb_typeof(v_item -> 'phase') is distinct from 'string'
|
|
or v_item ->> 'phase' not in ('initial', 'collecting_evidence', 'rescoring', 'ready', 'confirmed')
|
|
or pg_catalog.jsonb_typeof(v_item -> 'iteration') is distinct from 'number'
|
|
or v_item ->> 'iteration' !~ '^[0-9]+$'
|
|
or (v_item ->> 'iteration')::integer not between 0 and 100
|
|
or not public.conversational_rectification_text_array_is_bounded(
|
|
v_item -> 'notes', 20, 240, 8192
|
|
) then return false; end if;
|
|
end if;
|
|
return true;
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_declared_birth_input(
|
|
p_value jsonb
|
|
)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
declare
|
|
v_place jsonb;
|
|
v_source text;
|
|
v_key text;
|
|
v_before integer;
|
|
v_after integer;
|
|
begin
|
|
if pg_catalog.jsonb_typeof(p_value) is distinct from 'object'
|
|
or pg_catalog.octet_length(p_value::text) > 12000
|
|
or public.conversational_rectification_numbers_are_stable(p_value) is not true
|
|
or not public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array[
|
|
'birthDate', 'source', 'birthTimeClue', 'birthplace', 'reportedTime',
|
|
'reportedPeriod', 'uncertaintyBeforeMinutes', 'uncertaintyAfterMinutes'
|
|
]::text[]
|
|
)
|
|
or not (p_value ?& array['birthDate', 'source', 'birthTimeClue', 'birthplace']::text[])
|
|
or pg_catalog.jsonb_typeof(p_value -> 'birthDate') is distinct from 'string'
|
|
or not public.conversational_rectification_valid_date_text(p_value ->> 'birthDate')
|
|
or pg_catalog.jsonb_typeof(p_value -> 'source') is distinct from 'string'
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'source'
|
|
) is not true
|
|
or p_value -> 'birthTimeClue' <> 'null'::jsonb and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'birthTimeClue') is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'birthTimeClue'
|
|
) not between 1 and 240
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'birthTimeClue'
|
|
) is not true
|
|
) then return false; end if;
|
|
|
|
v_place := p_value -> 'birthplace';
|
|
if pg_catalog.jsonb_typeof(v_place) is distinct from 'object'
|
|
or pg_catalog.octet_length(v_place::text) > 4096
|
|
or not public.conversational_rectification_has_only_keys(
|
|
v_place,
|
|
array[
|
|
'city', 'countryCode', 'provinceCode', 'cityCode', 'districtCode',
|
|
'latitude', 'longitude', 'timezoneOffset'
|
|
]::text[]
|
|
)
|
|
or not (v_place ? 'timezoneOffset')
|
|
or not ((v_place ? 'city') or (v_place ? 'cityCode'))
|
|
or pg_catalog.jsonb_typeof(v_place -> 'timezoneOffset') is distinct from 'number'
|
|
or (v_place ->> 'timezoneOffset')::numeric not between -12 and 14
|
|
or (v_place ? 'latitude') <> (v_place ? 'longitude') then return false; end if;
|
|
foreach v_key in array array['city', 'provinceCode', 'cityCode', 'districtCode']::text[] loop
|
|
if v_place ? v_key and (
|
|
pg_catalog.jsonb_typeof(v_place -> v_key) is distinct from 'string'
|
|
or public.conversational_rectification_text_utf16_length(
|
|
v_place ->> v_key
|
|
) not between 1 and
|
|
case when v_key = 'city' then 120 else 80 end
|
|
or public.conversational_rectification_text_is_nonblank(
|
|
v_place ->> v_key
|
|
) is not true
|
|
) then return false; end if;
|
|
end loop;
|
|
if v_place ? 'countryCode' and (
|
|
pg_catalog.jsonb_typeof(v_place -> 'countryCode') is distinct from 'string'
|
|
or v_place ->> 'countryCode' !~ '^[A-Z0-9-]{1,8}$'
|
|
) then return false; end if;
|
|
if v_place ? 'latitude' and (
|
|
pg_catalog.jsonb_typeof(v_place -> 'latitude') is distinct from 'number'
|
|
or pg_catalog.jsonb_typeof(v_place -> 'longitude') is distinct from 'number'
|
|
or (v_place ->> 'latitude')::numeric not between -90 and 90
|
|
or (v_place ->> 'longitude')::numeric not between -180 and 180
|
|
) then return false; end if;
|
|
|
|
v_source := p_value ->> 'source';
|
|
if v_source not in (
|
|
'hospital_record', 'family_exact', 'approximate', 'period_only', 'unknown', 'legacy_import'
|
|
) then return false; end if;
|
|
if p_value ? 'reportedTime' and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'reportedTime') is distinct from 'string'
|
|
or not public.conversational_rectification_valid_time_text(p_value ->> 'reportedTime')
|
|
) then return false; end if;
|
|
if p_value ? 'reportedPeriod' and (
|
|
pg_catalog.jsonb_typeof(p_value -> 'reportedPeriod') is distinct from 'string'
|
|
or p_value ->> 'reportedPeriod' not in (
|
|
'early_morning', 'morning', 'afternoon', 'evening', 'late_night'
|
|
)
|
|
) then return false; end if;
|
|
if p_value ? 'uncertaintyBeforeMinutes' then
|
|
if pg_catalog.jsonb_typeof(p_value -> 'uncertaintyBeforeMinutes') is distinct from 'number'
|
|
or pg_catalog.jsonb_typeof(p_value -> 'uncertaintyAfterMinutes') is distinct from 'number'
|
|
or p_value ->> 'uncertaintyBeforeMinutes' !~ '^[0-9]+$'
|
|
or p_value ->> 'uncertaintyAfterMinutes' !~ '^[0-9]+$' then
|
|
return false;
|
|
end if;
|
|
v_before := (p_value ->> 'uncertaintyBeforeMinutes')::integer;
|
|
v_after := (p_value ->> 'uncertaintyAfterMinutes')::integer;
|
|
elsif p_value ? 'uncertaintyAfterMinutes' then
|
|
return false;
|
|
end if;
|
|
|
|
if v_source = 'hospital_record' then
|
|
return p_value ? 'reportedTime'
|
|
and not (p_value ? 'reportedPeriod')
|
|
and v_before = 2 and v_after = 2;
|
|
elsif v_source = 'family_exact' then
|
|
return p_value ? 'reportedTime'
|
|
and not (p_value ? 'reportedPeriod')
|
|
and v_before = v_after and v_before in (5, 10, 15);
|
|
elsif v_source = 'approximate' then
|
|
return p_value ? 'reportedTime'
|
|
and not (p_value ? 'reportedPeriod')
|
|
and v_before = v_after and v_before in (15, 30, 60);
|
|
elsif v_source = 'period_only' then
|
|
return p_value ? 'reportedPeriod'
|
|
and not (p_value ? 'reportedTime')
|
|
and not (p_value ? 'uncertaintyBeforeMinutes')
|
|
and not (p_value ? 'uncertaintyAfterMinutes');
|
|
elsif v_source = 'unknown' then
|
|
return not (p_value ? 'reportedPeriod')
|
|
and not (p_value ? 'reportedTime')
|
|
and not (p_value ? 'uncertaintyBeforeMinutes')
|
|
and not (p_value ? 'uncertaintyAfterMinutes');
|
|
end if;
|
|
return not ((p_value ? 'reportedTime') and (p_value ? 'reportedPeriod'))
|
|
and ((p_value ? 'reportedTime') or v_before is null)
|
|
and (v_before is null or (v_before between 0 and 720 and v_after between 0 and 720));
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_public_turn(p_value jsonb)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.jsonb_typeof(p_value) = 'object'
|
|
and pg_catalog.octet_length(p_value::text) <= 65536
|
|
and public.conversational_rectification_numbers_are_stable(p_value)
|
|
and public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array[
|
|
'caseId', 'journeyProtocol', 'status', 'turnVersion', 'narrative',
|
|
'candidate', 'technicalReceipt', 'evidenceRequest', 'evidenceRecap',
|
|
'actions', 'pendingConsultationQuestion'
|
|
]::text[]
|
|
)
|
|
and p_value ?& array[
|
|
'caseId', 'journeyProtocol', 'status', 'turnVersion', 'narrative',
|
|
'candidate', 'technicalReceipt', 'evidenceRequest', 'evidenceRecap',
|
|
'actions', 'pendingConsultationQuestion'
|
|
]::text[]
|
|
and pg_catalog.jsonb_typeof(p_value -> 'caseId') = 'string'
|
|
and public.conversational_rectification_valid_uuid_text(p_value ->> 'caseId')
|
|
and pg_catalog.jsonb_typeof(p_value -> 'journeyProtocol') = 'string'
|
|
and p_value ->> 'journeyProtocol' = 'conversational-evidence-v3'
|
|
and pg_catalog.jsonb_typeof(p_value -> 'status') = 'string'
|
|
and p_value ->> 'status' in ('active', 'paused', 'confirming', 'completed', 'abandoned')
|
|
and pg_catalog.jsonb_typeof(p_value -> 'turnVersion') = 'number'
|
|
and p_value ->> 'turnVersion' ~ '^[0-9]+$'
|
|
and pg_catalog.jsonb_typeof(p_value -> 'narrative') = 'string'
|
|
and public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'narrative'
|
|
) between 1 and 12000
|
|
and public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'narrative'
|
|
)
|
|
and public.conversational_rectification_valid_candidate(p_value -> 'candidate')
|
|
and public.conversational_rectification_valid_technical_receipt(p_value -> 'technicalReceipt')
|
|
and (
|
|
p_value -> 'evidenceRequest' = 'null'::jsonb
|
|
or public.conversational_rectification_valid_evidence_request(p_value -> 'evidenceRequest')
|
|
)
|
|
and public.conversational_rectification_valid_evidence_recap(p_value -> 'evidenceRecap')
|
|
and public.conversational_rectification_valid_actions(p_value -> 'actions')
|
|
and (
|
|
p_value -> 'pendingConsultationQuestion' = 'null'::jsonb
|
|
or (
|
|
pg_catalog.jsonb_typeof(p_value -> 'pendingConsultationQuestion') = 'string'
|
|
and public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'pendingConsultationQuestion'
|
|
) between 1 and 500
|
|
and public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'pendingConsultationQuestion'
|
|
)
|
|
)
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_action_request(p_value jsonb)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.jsonb_typeof(p_value) = 'object'
|
|
and pg_catalog.octet_length(p_value::text) <= 2048
|
|
and public.conversational_rectification_numbers_are_stable(p_value)
|
|
and public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array[
|
|
'kind', 'userId', 'caseId', 'expectedVersion', 'actionId',
|
|
'requestFingerprint', 'commandFingerprint'
|
|
]::text[]
|
|
)
|
|
and p_value ?& array[
|
|
'kind', 'userId', 'caseId', 'expectedVersion', 'actionId', 'requestFingerprint'
|
|
]::text[]
|
|
and pg_catalog.jsonb_typeof(p_value -> 'kind') = 'string'
|
|
and p_value ->> 'kind' in (
|
|
'create', 'save_turn', 'pause', 'abandon', 'confirm', 'import_legacy',
|
|
'reserve_fee', 'complete_fee', 'release_fee', 'recover_fee'
|
|
)
|
|
and pg_catalog.jsonb_typeof(p_value -> 'userId') = 'string'
|
|
and public.conversational_rectification_valid_uuid_text(p_value ->> 'userId')
|
|
and pg_catalog.jsonb_typeof(p_value -> 'caseId') = 'string'
|
|
and public.conversational_rectification_valid_uuid_text(p_value ->> 'caseId')
|
|
and pg_catalog.jsonb_typeof(p_value -> 'actionId') = 'string'
|
|
and public.conversational_rectification_valid_uuid_text(p_value ->> 'actionId')
|
|
and pg_catalog.jsonb_typeof(p_value -> 'expectedVersion') = 'number'
|
|
and p_value ->> 'expectedVersion' ~ '^[0-9]+$'
|
|
and pg_catalog.jsonb_typeof(p_value -> 'requestFingerprint') = 'string'
|
|
and p_value ->> 'requestFingerprint' ~ '^[0-9a-f]{64}$'
|
|
and (
|
|
not (p_value ? 'commandFingerprint')
|
|
or (
|
|
pg_catalog.jsonb_typeof(p_value -> 'commandFingerprint') = 'string'
|
|
and p_value ->> 'commandFingerprint' ~ '^[0-9a-f]{64}$'
|
|
)
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_action_request(
|
|
p_kind text,
|
|
p_user_id uuid,
|
|
p_case_id uuid,
|
|
p_expected_version bigint,
|
|
p_action_id uuid,
|
|
p_request_fingerprint text
|
|
)
|
|
returns jsonb
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.jsonb_build_object(
|
|
'kind', p_kind,
|
|
'userId', p_user_id,
|
|
'caseId', p_case_id,
|
|
'expectedVersion', p_expected_version,
|
|
'actionId', p_action_id,
|
|
'requestFingerprint', p_request_fingerprint
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.conversational_rectification_valid_action_response(
|
|
p_value jsonb,
|
|
p_action_kind text
|
|
)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
begin
|
|
if p_action_kind in ('reserve_fee', 'complete_fee', 'release_fee', 'recover_fee') then
|
|
return coalesce(pg_catalog.jsonb_typeof(p_value) = 'object'
|
|
and pg_catalog.octet_length(p_value::text) <= 2048
|
|
and public.conversational_rectification_numbers_are_stable(p_value)
|
|
and public.conversational_rectification_has_only_keys(
|
|
p_value, array['success', 'credits', 'billing_state', 'error_code']::text[]
|
|
)
|
|
and p_value ?& array['success', 'credits', 'billing_state', 'error_code']::text[]
|
|
and pg_catalog.jsonb_typeof(p_value -> 'success') = 'boolean'
|
|
and (
|
|
p_value -> 'credits' = 'null'::jsonb
|
|
or (
|
|
pg_catalog.jsonb_typeof(p_value -> 'credits') = 'number'
|
|
and p_value ->> 'credits' ~ '^[0-9]+$'
|
|
)
|
|
)
|
|
and (
|
|
p_value -> 'billing_state' = 'null'::jsonb
|
|
or (
|
|
pg_catalog.jsonb_typeof(p_value -> 'billing_state') = 'string'
|
|
and p_value ->> 'billing_state' in (
|
|
'reserved', 'charged', 'released', 'migration_waived'
|
|
)
|
|
)
|
|
)
|
|
and (
|
|
p_value -> 'error_code' = 'null'::jsonb
|
|
or (
|
|
pg_catalog.jsonb_typeof(p_value -> 'error_code') = 'string'
|
|
and public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'error_code'
|
|
) between 1 and 80
|
|
and public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'error_code'
|
|
)
|
|
)
|
|
), false);
|
|
end if;
|
|
return coalesce(pg_catalog.jsonb_typeof(p_value) = 'object'
|
|
and pg_catalog.octet_length(p_value::text) <= 69632
|
|
and public.conversational_rectification_numbers_are_stable(p_value)
|
|
and public.conversational_rectification_has_only_keys(
|
|
p_value,
|
|
array[
|
|
'case_id', 'user_id', 'status', 'turn_version', 'revision_of_case_id',
|
|
'imported_from_case_id', 'baseline_active_time', 'pending_consultation_question',
|
|
'billing_state', 'latest_turn'
|
|
]::text[]
|
|
)
|
|
and p_value ?& array[
|
|
'case_id', 'user_id', 'status', 'turn_version', 'revision_of_case_id',
|
|
'imported_from_case_id', 'baseline_active_time', 'pending_consultation_question',
|
|
'billing_state', 'latest_turn'
|
|
]::text[]
|
|
and pg_catalog.jsonb_typeof(p_value -> 'case_id') = 'string'
|
|
and public.conversational_rectification_valid_uuid_text(p_value ->> 'case_id')
|
|
and pg_catalog.jsonb_typeof(p_value -> 'user_id') = 'string'
|
|
and public.conversational_rectification_valid_uuid_text(p_value ->> 'user_id')
|
|
and pg_catalog.jsonb_typeof(p_value -> 'status') = 'string'
|
|
and p_value ->> 'status' in ('starting', 'active', 'paused', 'confirming', 'completed', 'abandoned')
|
|
and pg_catalog.jsonb_typeof(p_value -> 'turn_version') = 'number'
|
|
and p_value ->> 'turn_version' ~ '^[0-9]+$'
|
|
and (p_value -> 'revision_of_case_id' = 'null'::jsonb
|
|
or (pg_catalog.jsonb_typeof(p_value -> 'revision_of_case_id') = 'string'
|
|
and public.conversational_rectification_valid_uuid_text(
|
|
p_value ->> 'revision_of_case_id'
|
|
)))
|
|
and (p_value -> 'imported_from_case_id' = 'null'::jsonb
|
|
or (pg_catalog.jsonb_typeof(p_value -> 'imported_from_case_id') = 'string'
|
|
and public.conversational_rectification_valid_uuid_text(
|
|
p_value ->> 'imported_from_case_id'
|
|
)))
|
|
and (p_value -> 'baseline_active_time' = 'null'::jsonb
|
|
or (pg_catalog.jsonb_typeof(p_value -> 'baseline_active_time') = 'string'
|
|
and public.conversational_rectification_valid_time_text(
|
|
p_value ->> 'baseline_active_time'
|
|
)))
|
|
and (p_value -> 'pending_consultation_question' = 'null'::jsonb
|
|
or (pg_catalog.jsonb_typeof(p_value -> 'pending_consultation_question') = 'string'
|
|
and public.conversational_rectification_text_utf16_length(
|
|
p_value ->> 'pending_consultation_question'
|
|
) between 1 and 500
|
|
and public.conversational_rectification_text_is_nonblank(
|
|
p_value ->> 'pending_consultation_question'
|
|
)))
|
|
and (p_value -> 'billing_state' = 'null'::jsonb
|
|
or (pg_catalog.jsonb_typeof(p_value -> 'billing_state') = 'string'
|
|
and p_value ->> 'billing_state' in (
|
|
'reserved', 'charged', 'released', 'migration_waived'
|
|
)))
|
|
and public.conversational_rectification_valid_public_turn(p_value -> 'latest_turn'), false);
|
|
exception when others then
|
|
return false;
|
|
end;
|
|
$$;
|
|
|
|
alter table public.birth_time_rectification_cases
|
|
add column if not exists revision_of_case_id uuid,
|
|
add column if not exists imported_from_case_id uuid,
|
|
add column if not exists baseline_active_time time without time zone,
|
|
add column if not exists pending_consultation_question text,
|
|
add column if not exists declared_birth_input jsonb not null default '{}'::jsonb;
|
|
|
|
alter table public.birth_time_rectification_cases
|
|
drop constraint if exists birth_time_rectification_cases_revision_of_case_id_fkey,
|
|
drop constraint if exists birth_time_rectification_cases_imported_from_case_id_fkey,
|
|
drop constraint if exists birth_time_rectification_cases_pending_question_check,
|
|
drop constraint if exists birth_time_rectification_cases_declared_birth_input_check,
|
|
drop constraint if exists birth_time_rectification_cases_private_candidate_v3_check,
|
|
drop constraint if exists birth_time_rectification_cases_turn_state_v3_check,
|
|
drop constraint if exists birth_time_rectification_cases_journey_snapshot_v3_check,
|
|
drop constraint if exists birth_time_rectification_cases_journey_protocol_check,
|
|
drop constraint if exists birth_time_rectification_cases_status_check;
|
|
|
|
alter table public.birth_time_rectification_cases
|
|
add constraint birth_time_rectification_cases_revision_of_case_id_fkey
|
|
foreign key (revision_of_case_id)
|
|
references public.birth_time_rectification_cases(id) on delete set null,
|
|
add constraint birth_time_rectification_cases_imported_from_case_id_fkey
|
|
foreign key (imported_from_case_id)
|
|
references public.birth_time_rectification_cases(id) on delete set null,
|
|
add constraint birth_time_rectification_cases_pending_question_check
|
|
check (
|
|
pending_consultation_question is null
|
|
or (
|
|
public.conversational_rectification_text_utf16_length(
|
|
pending_consultation_question
|
|
) between 1 and 500
|
|
and public.conversational_rectification_text_is_nonblank(
|
|
pending_consultation_question
|
|
)
|
|
)
|
|
),
|
|
add constraint birth_time_rectification_cases_declared_birth_input_check
|
|
check (
|
|
jsonb_typeof(declared_birth_input) = 'object'
|
|
and octet_length(declared_birth_input::text) <= 12000
|
|
and (
|
|
journey_protocol <> 'conversational-evidence-v3'
|
|
or public.conversational_rectification_valid_declared_birth_input(declared_birth_input) is true
|
|
)
|
|
),
|
|
add constraint birth_time_rectification_cases_private_candidate_v3_check
|
|
check (
|
|
journey_protocol <> 'conversational-evidence-v3'
|
|
or public.conversational_rectification_valid_private_candidate(candidate_result) is true
|
|
),
|
|
add constraint birth_time_rectification_cases_turn_state_v3_check
|
|
check (
|
|
journey_protocol <> 'conversational-evidence-v3'
|
|
or public.conversational_rectification_valid_public_turn(turn_state) is true
|
|
),
|
|
add constraint birth_time_rectification_cases_journey_snapshot_v3_check
|
|
check (
|
|
journey_protocol <> 'conversational-evidence-v3'
|
|
or public.conversational_rectification_valid_public_turn(journey_snapshot) is true
|
|
),
|
|
add constraint birth_time_rectification_cases_journey_protocol_check
|
|
check (journey_protocol in (
|
|
'legacy-guided-v1',
|
|
'dynamic-choice-v2',
|
|
'conversational-evidence-v3'
|
|
)),
|
|
add constraint birth_time_rectification_cases_status_check
|
|
check (status in (
|
|
'assessing', 'rectifying', 'candidate', 'confirming', 'confirmed',
|
|
'starting', 'active', 'paused', 'completed', 'abandoned'
|
|
));
|
|
|
|
-- Billing and case creation are phases of one public start action. Billing
|
|
-- phases use deterministic internal receipt ids so the public action can stay
|
|
-- stable without colliding with the case-transition receipt key.
|
|
create or replace function public.conversational_rectification_billing_receipt_action_id(
|
|
p_action_id uuid,
|
|
p_action_kind text
|
|
)
|
|
returns uuid
|
|
language sql
|
|
immutable
|
|
strict
|
|
set search_path = ''
|
|
as $$
|
|
select pg_catalog.md5(p_action_id::text || ':' || p_action_kind)::uuid;
|
|
$$;
|
|
|
|
create table if not exists public.birth_time_rectification_turns (
|
|
id uuid not null default gen_random_uuid(),
|
|
case_id uuid not null
|
|
references public.birth_time_rectification_cases(id) on delete cascade,
|
|
turn_version bigint not null check (turn_version >= 0),
|
|
narrative text not null check (
|
|
public.conversational_rectification_text_utf16_length(narrative) between 1 and 12000
|
|
and public.conversational_rectification_text_is_nonblank(narrative)
|
|
),
|
|
candidate jsonb not null check (
|
|
public.conversational_rectification_valid_candidate(candidate) is true
|
|
),
|
|
technical_receipt jsonb not null check (
|
|
public.conversational_rectification_valid_technical_receipt(technical_receipt) is true
|
|
),
|
|
evidence_request jsonb check (
|
|
evidence_request is null
|
|
or public.conversational_rectification_valid_evidence_request(evidence_request) is true
|
|
),
|
|
evidence_recap jsonb not null default '[]'::jsonb check (
|
|
public.conversational_rectification_valid_evidence_recap(evidence_recap) is true
|
|
),
|
|
actions jsonb not null default '[]'::jsonb check (
|
|
public.conversational_rectification_valid_actions(actions) is true
|
|
),
|
|
output_validation_receipt jsonb not null check (
|
|
public.conversational_rectification_valid_validation_receipt(output_validation_receipt) is true
|
|
),
|
|
created_at timestamptz not null default now(),
|
|
primary key (case_id, turn_version),
|
|
unique (case_id, id),
|
|
check (not jsonb_path_exists(
|
|
jsonb_build_object(
|
|
'candidate', candidate,
|
|
'technicalReceipt', technical_receipt,
|
|
'evidenceRequest', evidence_request,
|
|
'evidenceRecap', evidence_recap,
|
|
'outputValidationReceipt', output_validation_receipt
|
|
),
|
|
'$.**.candidateWeights'
|
|
)),
|
|
check (not jsonb_path_exists(candidate, '$.**.candidateScores')),
|
|
check (not jsonb_path_exists(candidate, '$.**.partitionId')),
|
|
check (not jsonb_path_exists(technical_receipt, '$.**.rawModelOutput')),
|
|
check (not jsonb_path_exists(output_validation_receipt, '$.**.systemPrompt'))
|
|
);
|
|
|
|
create table if not exists public.birth_time_rectification_event_evidence (
|
|
id uuid primary key default gen_random_uuid(),
|
|
case_id uuid not null,
|
|
source_turn_id uuid not null,
|
|
raw_text text not null check (
|
|
public.conversational_rectification_text_utf16_length(raw_text) between 1 and 4000
|
|
and public.conversational_rectification_text_is_nonblank(raw_text)
|
|
),
|
|
domain text not null check (
|
|
domain in ('career', 'education', 'relocation', 'relationship', 'family', 'other')
|
|
),
|
|
event_summary text not null check (
|
|
public.conversational_rectification_text_utf16_length(event_summary) between 1 and 1000
|
|
and public.conversational_rectification_text_is_nonblank(event_summary)
|
|
),
|
|
date_value text check (
|
|
date_value is null
|
|
or (
|
|
public.conversational_rectification_text_utf16_length(date_value) between 1 and 80
|
|
and public.conversational_rectification_text_is_nonblank(date_value)
|
|
)
|
|
),
|
|
date_precision text not null check (
|
|
date_precision in ('day', 'month', 'year', 'range', 'unknown')
|
|
),
|
|
extraction_status text not null check (
|
|
extraction_status in ('clear', 'needs_clarification', 'corrected')
|
|
),
|
|
corrects_evidence_ids uuid[] not null default '{}'::uuid[] check (
|
|
pg_catalog.cardinality(corrects_evidence_ids) <= 1
|
|
and (
|
|
(extraction_status = 'corrected' and pg_catalog.cardinality(corrects_evidence_ids) = 1)
|
|
or (extraction_status = 'clear' and pg_catalog.cardinality(corrects_evidence_ids) = 0)
|
|
or extraction_status = 'needs_clarification'
|
|
)
|
|
),
|
|
scoreable boolean not null default false,
|
|
created_at timestamptz not null default now(),
|
|
foreign key (case_id, source_turn_id)
|
|
references public.birth_time_rectification_turns(case_id, id) on delete cascade
|
|
);
|
|
|
|
create table if not exists public.birth_time_rectification_action_receipts (
|
|
-- Billing reservation happens before the case exists, so receipts cannot
|
|
-- depend on the case row. The service-only RPCs preserve ownership.
|
|
case_id uuid not null,
|
|
action_id uuid not null,
|
|
user_id uuid not null references auth.users(id) on delete cascade,
|
|
action_kind text not null check (action_kind in (
|
|
'create', 'save_turn', 'pause', 'abandon', 'confirm', 'import_legacy',
|
|
'reserve_fee', 'complete_fee', 'release_fee', 'recover_fee'
|
|
)),
|
|
expected_turn_version bigint not null check (expected_turn_version >= 0),
|
|
result_turn_version bigint not null check (result_turn_version >= 0),
|
|
request_fingerprint text not null check (request_fingerprint ~ '^[0-9a-f]{64}$'),
|
|
request jsonb not null check (
|
|
public.conversational_rectification_valid_action_request(request) is true
|
|
),
|
|
response jsonb not null check (
|
|
public.conversational_rectification_valid_action_response(response, action_kind) is true
|
|
),
|
|
created_at timestamptz not null default now(),
|
|
primary key (case_id, action_id),
|
|
check (not jsonb_path_exists(response, '$.**.candidateWeights')),
|
|
check (not jsonb_path_exists(response, '$.**.candidateScores')),
|
|
check (not jsonb_path_exists(response, '$.**.partitionId')),
|
|
check (not jsonb_path_exists(response, '$.**.rawModelOutput')),
|
|
check (not jsonb_path_exists(response, '$.**.systemPrompt'))
|
|
);
|
|
|
|
create table if not exists public.birth_time_rectification_billing (
|
|
-- A reservation is the first durable start record and therefore precedes
|
|
-- case creation. The case transition RPC claims this row atomically.
|
|
case_id uuid primary key,
|
|
user_id uuid not null references auth.users(id) on delete cascade,
|
|
price integer not null check (price between 1 and 1000000),
|
|
state text not null check (
|
|
state in ('reserved', 'charged', 'released', 'migration_waived')
|
|
),
|
|
reservation_id uuid unique,
|
|
billing_receipt_id uuid unique,
|
|
reserve_action_id uuid,
|
|
complete_action_id uuid,
|
|
release_action_id uuid,
|
|
balance_after integer check (balance_after is null or balance_after >= 0),
|
|
reserved_at timestamptz,
|
|
charged_at timestamptz,
|
|
released_at timestamptz,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
check (
|
|
state not in ('reserved', 'charged')
|
|
or (reservation_id is not null and reserve_action_id is not null and reserved_at is not null)
|
|
),
|
|
check (
|
|
state <> 'charged'
|
|
or (billing_receipt_id is not null and complete_action_id is not null and charged_at is not null)
|
|
),
|
|
check (state <> 'released' or (release_action_id is not null and released_at is not null)),
|
|
unique (user_id, reserve_action_id)
|
|
);
|
|
|
|
create index if not exists birth_time_rectification_cases_v3_account_idx
|
|
on public.birth_time_rectification_cases (user_id, updated_at desc)
|
|
where journey_protocol = 'conversational-evidence-v3';
|
|
create index if not exists birth_time_rectification_evidence_case_idx
|
|
on public.birth_time_rectification_event_evidence (case_id, created_at);
|
|
create unique index if not exists birth_time_rectification_reserve_action_idx
|
|
on public.birth_time_rectification_action_receipts (user_id, action_id)
|
|
where action_kind = 'reserve_fee';
|
|
|
|
alter table public.birth_time_rectification_turns enable row level security;
|
|
alter table public.birth_time_rectification_event_evidence enable row level security;
|
|
alter table public.birth_time_rectification_action_receipts enable row level security;
|
|
alter table public.birth_time_rectification_billing enable row level security;
|
|
|
|
revoke all on table public.birth_time_rectification_cases from public, anon, authenticated;
|
|
revoke all on table public.birth_time_rectification_turns from public, anon, authenticated;
|
|
revoke all on table public.birth_time_rectification_event_evidence from public, anon, authenticated;
|
|
revoke all on table public.birth_time_rectification_action_receipts from public, anon, authenticated;
|
|
revoke all on table public.birth_time_rectification_billing from public, anon, authenticated;
|
|
|
|
revoke all on table public.birth_time_rectification_cases from service_role;
|
|
revoke all on table public.birth_time_rectification_turns from service_role;
|
|
revoke all on table public.birth_time_rectification_event_evidence from service_role;
|
|
revoke all on table public.birth_time_rectification_action_receipts from service_role;
|
|
revoke all on table public.birth_time_rectification_billing from service_role;
|
|
|
|
grant all on table public.birth_time_rectification_cases to service_role;
|
|
grant all on table public.birth_time_rectification_turns to service_role;
|
|
grant all on table public.birth_time_rectification_event_evidence to service_role;
|
|
grant all on table public.birth_time_rectification_action_receipts to service_role;
|
|
grant all on table public.birth_time_rectification_billing to service_role;
|
|
|
|
revoke all on function public.conversational_rectification_billing_receipt_action_id(
|
|
uuid, text
|
|
) from public, anon, authenticated, service_role;
|
|
revoke all on function public.conversational_rectification_numbers_are_stable(jsonb)
|
|
from public, anon, authenticated;
|
|
revoke all on function public.conversational_rectification_text_utf16_length(text)
|
|
from public, anon, authenticated;
|
|
revoke all on function public.conversational_rectification_text_is_nonblank(text)
|
|
from public, anon, authenticated;
|
|
revoke all on function public.conversational_rectification_valid_life_event_evidence(jsonb)
|
|
from public, anon, authenticated;
|
|
revoke all on function public.conversational_rectification_valid_life_event_evidence_array(jsonb)
|
|
from public, anon, authenticated;
|
|
grant execute on function public.conversational_rectification_numbers_are_stable(jsonb)
|
|
to service_role;
|
|
grant execute on function public.conversational_rectification_text_utf16_length(text)
|
|
to service_role;
|
|
grant execute on function public.conversational_rectification_text_is_nonblank(text)
|
|
to service_role;
|
|
grant execute on function public.conversational_rectification_valid_life_event_evidence(jsonb)
|
|
to service_role;
|
|
grant execute on function public.conversational_rectification_valid_life_event_evidence_array(jsonb)
|
|
to service_role;
|
|
|
|
commit;
|