unis_crm/sql/20260713_fix_crm_codes_pg17...

154 lines
5.0 KiB
PL/PgSQL

-- Fix historical customer-code duplicates before restoring the intended constraint.
-- The first row keeps its business code; later duplicates receive a traceable migration code.
begin;
set local lock_timeout = '10s';
set local statement_timeout = '5min';
select pg_advisory_xact_lock(hashtext('20260713_fix_crm_codes_pg17'));
with ranked_customer_codes as (
select
id,
row_number() over (partition by customer_code order by created_at nulls last, id) as row_no
from crm_customer
where customer_code is not null
and btrim(customer_code) <> ''
)
update crm_customer customer
set customer_code = 'CUS-MIG-' || customer.id::text,
updated_at = now()
from ranked_customer_codes duplicate
where duplicate.id = customer.id
and duplicate.row_no > 1;
do $$
declare
constraint_definition text;
begin
select pg_get_constraintdef(oid)
into constraint_definition
from pg_constraint
where conrelid = 'crm_customer'::regclass
and conname = 'uk_crm_customer_code';
if constraint_definition is not null
and constraint_definition not ilike 'UNIQUE (customer_code)%' then
alter table crm_customer drop constraint uk_crm_customer_code;
constraint_definition := null;
end if;
if constraint_definition is null then
alter table crm_customer
add constraint uk_crm_customer_code unique (customer_code);
end if;
end $$;
create sequence if not exists crm_customer_code_seq start with 1 increment by 1 minvalue 1;
create sequence if not exists crm_opportunity_code_seq start with 1 increment by 1 minvalue 1;
-- Lock sequence consumers before reading their current values.
alter sequence crm_customer_code_seq no cycle;
alter sequence crm_opportunity_code_seq no cycle;
do $$
declare
sales_id_sequence regclass;
channel_id_sequence regclass;
begin
if to_regclass('crm_sales_expansion') is not null then
sales_id_sequence := pg_get_serial_sequence('crm_sales_expansion', 'id')::regclass;
end if;
if to_regclass('crm_channel_expansion') is not null then
channel_id_sequence := pg_get_serial_sequence('crm_channel_expansion', 'id')::regclass;
end if;
if sales_id_sequence is not null then
execute format('alter sequence %s no cycle', sales_id_sequence);
end if;
if channel_id_sequence is not null then
execute format('alter sequence %s no cycle', channel_id_sequence);
end if;
end $$;
-- ALTER SEQUENCE RESTART is transactional; setval() would survive a rollback.
do $$
declare
customer_code_next bigint;
opportunity_code_next bigint;
begin
select greatest(
coalesce((
select max(substring(customer_code from '([0-9]+)$')::bigint) + 1
from crm_customer
where customer_code ~ '^CUS-[0-9]{8}-[0-9]+$'
), 1),
(select case when is_called then last_value + 1 else last_value end from crm_customer_code_seq)
)
into customer_code_next;
select greatest(
coalesce((
select max(substring(opportunity_code from '([0-9]+)$')::bigint) + 1
from crm_opportunity
where opportunity_code ~ '^OPP-[0-9]{8}-[0-9]+$'
), 1),
(select case when is_called then last_value + 1 else last_value end from crm_opportunity_code_seq)
)
into opportunity_code_next;
execute format('alter sequence crm_customer_code_seq restart with %s', customer_code_next);
execute format('alter sequence crm_opportunity_code_seq restart with %s', opportunity_code_next);
end $$;
do $$
declare
sales_id_sequence regclass;
channel_id_sequence regclass;
sales_id_max bigint;
sales_id_current_next bigint;
channel_id_max bigint;
channel_id_current_next bigint;
begin
if to_regclass('crm_sales_expansion') is not null then
sales_id_sequence := pg_get_serial_sequence('crm_sales_expansion', 'id')::regclass;
end if;
if to_regclass('crm_channel_expansion') is not null then
channel_id_sequence := pg_get_serial_sequence('crm_channel_expansion', 'id')::regclass;
end if;
if sales_id_sequence is not null then
select coalesce(max(id), 0) into sales_id_max from crm_sales_expansion;
if sales_id_max = 9223372036854775807 then
raise exception 'crm_sales_expansion identity sequence has exhausted bigint values';
end if;
execute format(
'select case when is_called then last_value + 1 else last_value end from %s',
sales_id_sequence
) into sales_id_current_next;
execute format(
'alter sequence %s restart with %s',
sales_id_sequence,
greatest(sales_id_max + 1, sales_id_current_next)
);
end if;
if channel_id_sequence is not null then
select coalesce(max(id), 0) into channel_id_max from crm_channel_expansion;
if channel_id_max = 9223372036854775807 then
raise exception 'crm_channel_expansion identity sequence has exhausted bigint values';
end if;
execute format(
'select case when is_called then last_value + 1 else last_value end from %s',
channel_id_sequence
) into channel_id_current_next;
execute format(
'alter sequence %s restart with %s',
channel_id_sequence,
greatest(channel_id_max + 1, channel_id_current_next)
);
end if;
end $$;
commit;