154 lines
5.0 KiB
PL/PgSQL
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;
|