How correctly implement and enforce a default option among a list of rows is always present in postgresql
-
15-03-2021 - |
سؤال
I have a list of address, and need to set which one is the default.
This look easy:
CREATE TABLE address (
address_code TEXT PRIMARY KEY CHECK (not_empty(address_code)),
customer_code TEXT NOT NULL CHECK (not_empty(customer_code)),
place_code TEXT NOT NULL CHECK (not_empty(place_code)), //ie: House, Main branch...
is_default BOOLEAN NOT NULL DEFAULT FALSE,
);
CREATE UNIQUE INDEX idx_address_is_default ON address (customer_code, is_default) WHERE (is_default = true);
However, if the default address is deleted or other address is set to be default, it must select other row, ie: I need to always have a default address (as long exist rows. Is ok to have zero).
For now I have a trigger:
CREATE OR REPLACE FUNCTION address_change_delete()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $function$
BEGIN
IF OLD.is_default THEN
--RAISE NOTICE 'DEL:%:%', OLD.is_default, NEW.is_default;
UPDATE address SET is_default = true WHERE customer_code = OLD.customer_code AND address_code IN
(SELECT address_code FROM address WHERE customer_code = OLD.customer_code AND place_code <> OLD.place_code LIMIT 1);
END IF;
RETURN OLD;
END;
$function$;
CREATE OR REPLACE FUNCTION address_change_update()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $function$
BEGIN
IF EXISTS (SELECT 1 FROM config
WHERE name = CAST(pg_backend_pid() AS VARCHAR)) THEN
--RAISE NOTICE 'REC:%', pg_backend_pid();
-- it's recursing
RETURN NEW;
END IF;
INSERT INTO config (name, value) VALUES (CAST(pg_backend_pid() AS VARCHAR), true);
IF TG_OP = 'UPDATE' THEN
IF OLD.is_default AND NEW.is_default = false THEN
--RAISE NOTICE 'UP:%:%', OLD.is_default, NEW.is_default;
UPDATE address SET is_default = true WHERE customer_code = NEW.customer_code AND address_code IN
(SELECT address_code FROM address WHERE customer_code = NEW.customer_code AND place_code <> NEW.place_code LIMIT 1);
END IF;
END IF;
IF NEW.is_default AND (TG_OP = 'INSERT' OR OLD.is_default = false) THEN
--RAISE NOTICE 'IN:%:%', OLD.is_default, NEW.is_default;
UPDATE address SET is_default = false WHERE customer_code = NEW.customer_code AND address_code IN
(SELECT address_code FROM address WHERE customer_code = NEW.customer_code AND place_code <> NEW.place_code);
END IF;
DELETE FROM config WHERE name = CAST(pg_backend_pid() AS VARCHAR);
RETURN NEW;
END;
$function$;
CREATE TRIGGER address_change_delete
AFTER DELETE ON
address FOR EACH ROW
EXECUTE PROCEDURE address_change_delete();
CREATE TRIGGER address_change
BEFORE INSERT OR UPDATE ON
address FOR EACH ROW
EXECUTE PROCEDURE address_change_update();
Now this is causing me a lot of troubles and wonder if I'm overlooking something.
P.D: This come from data from outside, so I can't rely in a user to pick a address for me, that is why must be automated...
لا يوجد حل صحيح
لا تنتمي إلى dba.stackexchange