| | 139 | DO $$ |
| | 140 | BEGIN |
| | 141 | IF NOT EXISTS ( |
| | 142 | SELECT 1 |
| | 143 | FROM information_schema.columns |
| | 144 | WHERE table_name = 'appointments' |
| | 145 | AND column_name = 'slot' |
| | 146 | ) THEN |
| | 147 | ALTER TABLE appointments |
| | 148 | ADD COLUMN slot tstzrange |
| | 149 | GENERATED ALWAYS AS ( |
| | 150 | tstzrange(date_time, date_time + interval '30 minutes', '[)') |
| | 151 | ) STORED; |
| | 152 | END IF; |
| | 153 | END $$; |
| | 154 | |
| | 155 | CREATE OR REPLACE FUNCTION petify_trg_appointments_no_overlap() |
| | 156 | RETURNS trigger |
| | 157 | LANGUAGE plpgsql |
| | 158 | AS $$ |
| | 159 | BEGIN |
| | 160 | |
| | 161 | IF NEW.status NOT IN ('CONFIRMED','DONE') THEN |
| | 162 | RETURN NEW; |
| | 163 | END IF; |
| | 164 | |
| | 165 | |
| | 166 | IF EXISTS ( |
| | 167 | SELECT 1 |
| | 168 | FROM appointments a |
| | 169 | WHERE a.responsible_owner_id = NEW.responsible_owner_id |
| | 170 | AND a.status IN ('CONFIRMED','DONE') |
| | 171 | AND a.slot && NEW.slot |
| | 172 | AND (TG_OP <> 'UPDATE' OR a.appointment_id <> NEW.appointment_id) |
| | 173 | ) THEN |
| | 174 | RAISE EXCEPTION |
| | 175 | 'Overlapping appointment for owner % at %', |
| | 176 | NEW.responsible_owner_id, NEW.date_time; |
| | 177 | END IF; |
| | 178 | |
| | 179 | |
| | 180 | IF EXISTS ( |
| | 181 | SELECT 1 |
| | 182 | FROM appointments a |
| | 183 | WHERE a.animal_id = NEW.animal_id |
| | 184 | AND a.status IN ('CONFIRMED','DONE') |
| | 185 | AND a.slot && NEW.slot |
| | 186 | AND (TG_OP <> 'UPDATE' OR a.appointment_id <> NEW.appointment_id) |
| | 187 | ) THEN |
| | 188 | RAISE EXCEPTION |
| | 189 | 'Overlapping appointment for animal % at %', |
| | 190 | NEW.animal_id, NEW.date_time; |
| | 191 | END IF; |
| | 192 | |
| | 193 | RETURN NEW; |
| | 194 | END; |
| | 195 | $$; |
| | 196 | |
| | 197 | |
| 145 | | }}} |
| 146 | | {{{Stored procedures}}} |
| 147 | | {{{ |
| 148 | | -- 30min timeslots |
| 149 | | CREATE EXTENSION IF NOT EXISTS btree_gist; |
| 150 | | |
| 151 | | DO $$ |
| 152 | | BEGIN |
| 153 | | IF NOT EXISTS ( |
| 154 | | SELECT 1 |
| 155 | | FROM information_schema.columns |
| 156 | | WHERE table_name='appointments' AND column_name='slot' |
| 157 | | ) THEN |
| 158 | | ALTER TABLE appointments |
| 159 | | ADD COLUMN slot tstzrange |
| 160 | | GENERATED ALWAYS AS (tstzrange(date_time, date_time + interval '30 minutes', '[)')) STORED; |
| 161 | | END IF; |
| 162 | | END $$; |
| 163 | | |
| 164 | | DO $$ |
| 165 | | BEGIN |
| 166 | | IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'appointments_no_overlap_per_owner') THEN |
| 167 | | ALTER TABLE appointments |
| 168 | | ADD CONSTRAINT appointments_no_overlap_per_owner |
| 169 | | EXCLUDE USING gist (responsible_owner_id WITH =, slot WITH &&) |
| 170 | | WHERE (status IN ('CONFIRMED','DONE')); |
| 171 | | END IF; |
| 172 | | END $$; |
| 173 | | |
| 174 | | DO $$ |
| 175 | | BEGIN |
| 176 | | IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'appointments_no_overlap_per_animal') THEN |
| 177 | | ALTER TABLE appointments |
| 178 | | ADD CONSTRAINT appointments_no_overlap_per_animal |
| 179 | | EXCLUDE USING gist (animal_id WITH =, slot WITH &&) |
| 180 | | WHERE (status IN ('CONFIRMED','DONE')); |
| 181 | | END IF; |
| 182 | | END $$; |
| 183 | | |
| 184 | | }}} |
| | 204 | |
| | 205 | DROP TRIGGER IF EXISTS trg_appointments_no_overlap ON appointments; |
| | 206 | CREATE TRIGGER trg_appointments_no_overlap |
| | 207 | BEFORE INSERT OR UPDATE |
| | 208 | ON appointments |
| | 209 | FOR EACH ROW |
| | 210 | EXECUTE FUNCTION petify_trg_appointments_no_overlap(); |
| | 211 | |
| | 212 | }}} |
| | 213 | |