| | 273 | |
| | 274 | = Тригери = |
| | 275 | |
| | 276 | |
| | 277 | === Тригер 1: Валидација на хиерархија на фрагменти (trg_fragment_check) === |
| | 278 | |
| | 279 | 1. **Намена:** |
| | 280 | Тригерот `trg_fragment_check` е наменет за автоматска валидација на записите во табелата `Fragments`. Неговата главна цел е да спречи внесување или ажурирање на фрагмент кој истовремено е директно поврзан со археолошки предмет (`object_id`) и со друг фрагмент (`parent_fragment_id`). |
| | 281 | |
| | 282 | 2. **Случај на употреба:** |
| | 283 | Во системот за управување со културно-археолошко наследство, фрагментите имаат важна улога затоа што претставуваат делови од артефакти, остатоци од предмети или под-фрагменти кои се пронајдени на локалитет. Поради тоа, секој фрагмент мора да има јасна логичка припадност. |
| | 284 | |
| | 285 | Фрагментот може: |
| | 286 | * директно да припаѓа на археолошки предмет преку `object_id` |
| | 287 | * да биде дел од друг фрагмент преку `parent_fragment_id` |
| | 288 | |
| | 289 | Но не смее истовремено да припаѓа и на предмет и на друг фрагмент, бидејќи тоа би создало нејасна и двојна хиерархија. |
| | 290 | |
| | 291 | 3. **Причина за избор на тригер:** |
| | 292 | Овој тригер е избран затоа што се работи за доменско правило кое мора да се провери автоматски пред секој внес или ажурирање. Иако во табелата `Fragments` постои и `CHECK` ограничување за ова правило, тригерот дополнително овозможува појасна контрола и експлицитна порака за грешка. |
| | 293 | |
| | 294 | Со ова се добива подобра разбирливост при тестирање и при работа со апликацијата, бидејќи корисникот добива конкретна порака: |
| | 295 | |
| | 296 | {{{ |
| | 297 | Фрагмент не може да има и object и parent |
| | 298 | }}} |
| | 299 | |
| | 300 | 4. **Имплементација:** |
| | 301 | |
| | 302 | Тригер функцијата е: |
| | 303 | |
| | 304 | {{{ |
| | 305 | CREATE OR REPLACE FUNCTION trg_fragment_validation() |
| | 306 | RETURNS TRIGGER AS $$ |
| | 307 | BEGIN |
| | 308 | IF NEW.object_id IS NOT NULL AND NEW.parent_fragment_id IS NOT NULL THEN |
| | 309 | RAISE EXCEPTION 'Фрагмент не може да има и object и parent'; |
| | 310 | END IF; |
| | 311 | |
| | 312 | RETURN NEW; |
| | 313 | END; |
| | 314 | $$ LANGUAGE plpgsql; |
| | 315 | }}} |
| | 316 | |
| | 317 | Тригерот се активира: |
| | 318 | |
| | 319 | {{{ |
| | 320 | CREATE TRIGGER trg_fragment_check |
| | 321 | BEFORE INSERT OR UPDATE ON Fragments |
| | 322 | FOR EACH ROW |
| | 323 | EXECUTE FUNCTION trg_fragment_validation(); |
| | 324 | }}} |
| | 325 | |
| | 326 | 5. **Логика на работа:** |
| | 327 | Тригерот се извршува пред секој `INSERT` или `UPDATE` врз табелата `Fragments`. Тој ја проверува новата вредност која треба да се внесе или ажурира преку `NEW`. |
| | 328 | |
| | 329 | Доколку и `NEW.object_id` и `NEW.parent_fragment_id` имаат вредност различна од `NULL`, записот се одбива и се фрла исклучок. |
| | 330 | |
| | 331 | Ова значи дека базата не дозволува фрагментот да има двојна припадност. |
| | 332 | |
| | 333 | 6. **Бизнис логика:** |
| | 334 | Овој тригер ја имплементира логиката за зачувување на археолошкиот контекст на фрагментите. Во реална археолошка евиденција, важно е точно да се знае дали еден фрагмент е директен дел од предмет или е под-фрагмент од друг фрагмент. |
| | 335 | |
| | 336 | Ако ова правило не постои, би можело да се случи ист фрагмент да биде прикажан во две различни структури, што би предизвикало погрешни извештаи, неточна реконструкција на предметите и неконзистентна научна документација. |
| | 337 | |
| | 338 | 7. **Пример на неправилен внес:** |
| | 339 | |
| | 340 | {{{ |
| | 341 | INSERT INTO Fragments (object_id, parent_fragment_id, status_id) |
| | 342 | VALUES (1, 2, 1); |
| | 343 | }}} |
| | 344 | |
| | 345 | Овој внес се одбива затоа што фрагментот има и `object_id` и `parent_fragment_id`. |
| | 346 | |
| | 347 | 8. **Заклучок:** |
| | 348 | `trg_fragment_check` е важен тригер за зачувување на правилната хиерархија на фрагментите. Тој спречува нелогични релации во археолошката документација и обезбедува секој фрагмент да има јасна и единствена припадност. |
| | 349 | |
| | 350 | |
| | 351 | |
| | 352 | === Тригер 2: Проверка на одобрен истражувачки пристап (trg_access_check) === |
| | 353 | |
| | 354 | 1. **Намена:** |
| | 355 | Тригерот `trg_access_check` е наменет за валидација на записите во табелата `Researcher_Access`. Неговата цел е да спречи внесување на одобрен истражувачки пристап доколку не е наведен конкретен археолошки предмет. |
| | 356 | |
| | 357 | 2. **Случај на употреба:** |
| | 358 | Во системот, истражувачите можат да побараат пристап до конкретни предмети за научна обработка, анализа или конзерваторска документација. Пристапот не смее да биде апстрактен или општ, туку мора да се однесува на точно определен артефакт. |
| | 359 | |
| | 360 | Одобрување пристап без конкретен предмет би било логички невалидно, бидејќи системот не би знаел до кој објект истражувачот добил дозвола. |
| | 361 | |
| | 362 | 3. **Причина за избор на тригер:** |
| | 363 | Тригерот е избран затоа што ова правило е поврзано со бизнис логиката на процесот на одобрување пристап. Иако `object_id` во табелата `Researcher_Access` е дефиниран како `NOT NULL`, тригерот додава дополнителна проверка за конкретен случај: кога статусот на пристап е одобрен. |
| | 364 | |
| | 365 | На тој начин правилото е експлицитно документирано во базата и директно се поврзува со статусот `access_status_id = 6`, кој во податоците се користи за статусот „Одобрено“. |
| | 366 | |
| | 367 | 4. **Имплементација:** |
| | 368 | |
| | 369 | Тригер функцијата е: |
| | 370 | |
| | 371 | {{{ |
| | 372 | CREATE OR REPLACE FUNCTION trg_block_access() |
| | 373 | RETURNS TRIGGER AS $$ |
| | 374 | BEGIN |
| | 375 | IF NEW.access_status_id = 6 AND NEW.object_id IS NULL THEN |
| | 376 | RAISE EXCEPTION 'Не може одобрен пристап без објект'; |
| | 377 | END IF; |
| | 378 | |
| | 379 | RETURN NEW; |
| | 380 | END; |
| | 381 | $$ LANGUAGE plpgsql; |
| | 382 | }}} |
| | 383 | |
| | 384 | Тригерот се активира: |
| | 385 | |
| | 386 | {{{ |
| | 387 | CREATE TRIGGER trg_access_check |
| | 388 | BEFORE INSERT ON Researcher_Access |
| | 389 | FOR EACH ROW |
| | 390 | EXECUTE FUNCTION trg_block_access(); |
| | 391 | }}} |
| | 392 | |
| | 393 | 5. **Логика на работа:** |
| | 394 | Тригерот се извршува пред внесување нов запис во `Researcher_Access`. |
| | 395 | |
| | 396 | Проверува дали: |
| | 397 | * `access_status_id = 6`, односно пристапот е одобрен |
| | 398 | * `object_id IS NULL`, односно не е наведен предмет |
| | 399 | |
| | 400 | Доколку двата услови се исполнети, внесот се блокира. |
| | 401 | |
| | 402 | 6. **Бизнис логика:** |
| | 403 | Овој тригер ја имплементира контролата на истражувачки пристап. Во контекст на културно наследство, предметите можат да имаат чувствителни информации, како точна локација на пронаоѓање, состојба, вредност, конзерваторски третмани или научна важност. |
| | 404 | |
| | 405 | Затоа секој одобрен пристап мора да биде поврзан со конкретен предмет. Ова спречува нејасни дозволи и овозможува прецизна ревизија на тоа кој корисник добил пристап до кој артефакт. |
| | 406 | |
| | 407 | 7. **Заклучок:** |
| | 408 | `trg_access_check` обезбедува дополнителна безбедност и логичка исправност во процесот на одобрување истражувачки пристап. Тој спречува креирање на невалидни записи и помага системот да одржи точна евиденција за дозволите на истражувачите. |
| | 409 | |
| | 410 | |
| | 411 | |
| | 412 | === Тригер 3: Спречување публикација без главен автор (trg_publication_check) === |
| | 413 | |
| | 414 | 1. **Намена:** |
| | 415 | Тригерот `trg_publication_check` е наменет за проверка на научните публикации пред нивно внесување или ажурирање. Неговата цел е да спречи публикација да постои без дефиниран главен автор. |
| | 416 | |
| | 417 | 2. **Случај на употреба:** |
| | 418 | Во системот се чуваат научни публикации поврзани со археолошки предмети. Публикацијата може да има повеќе автори преку табелата `Publication_Authors`, но мора да постои еден главен автор во табелата `Publications` преку полето `main_author_id`. |
| | 419 | |
| | 420 | Главниот автор е важен затоа што претставува примарно лице одговорно за публикацијата, нејзината содржина и академската евиденција. |
| | 421 | |
| | 422 | 3. **Причина за избор на тригер:** |
| | 423 | Тригерот е избран затоа што се работи за бизнис правило кое мора да важи и при внесување и при ажурирање на публикации. Иако ова правило може делумно да се реализира со `NOT NULL` constraint, тригерот овозможува експлицитна контрола и јасна порака за грешка. |
| | 424 | |
| | 425 | Дополнително, тригерот е корисен бидејќи во моделот `main_author_id` може да биде `NULL` на ниво на дефиниција на табела, но бизнис логиката бара да не се дозволува реално внесување публикација без главен автор. |
| | 426 | |
| | 427 | 4. **Имплементација:** |
| | 428 | |
| | 429 | Тригер функцијата е: |
| | 430 | |
| | 431 | {{{ |
| | 432 | CREATE OR REPLACE FUNCTION trg_pub_author_check() |
| | 433 | RETURNS TRIGGER AS $$ |
| | 434 | BEGIN |
| | 435 | IF NEW.main_author_id IS NULL THEN |
| | 436 | RAISE EXCEPTION 'Публикацијата мора да има главен автор'; |
| | 437 | END IF; |
| | 438 | |
| | 439 | RETURN NEW; |
| | 440 | END; |
| | 441 | $$ LANGUAGE plpgsql; |
| | 442 | }}} |
| | 443 | |
| | 444 | Тригерот се активира: |
| | 445 | |
| | 446 | {{{ |
| | 447 | CREATE TRIGGER trg_publication_check |
| | 448 | BEFORE INSERT OR UPDATE ON Publications |
| | 449 | FOR EACH ROW |
| | 450 | EXECUTE FUNCTION trg_pub_author_check(); |
| | 451 | }}} |
| | 452 | |
| | 453 | 5. **Логика на работа:** |
| | 454 | Тригерот се извршува пред секој `INSERT` или `UPDATE` врз табелата `Publications`. |
| | 455 | |
| | 456 | Доколку новата вредност за `main_author_id` е `NULL`, внесот или ажурирањето се прекинува и се фрла исклучок. |
| | 457 | |
| | 458 | 6. **Бизнис логика:** |
| | 459 | Овој тригер ја имплементира академската логика на системот. Во археолошки и научен контекст, секоја публикација мора да има одговорен автор. |
| | 460 | |
| | 461 | Без ова правило, системот би можел да содржи публикации без јасна одговорност, што би довело до проблеми при цитирање, библиографска обработка и научна документација. |
| | 462 | |
| | 463 | 7. **Заклучок:** |
| | 464 | `trg_publication_check` обезбедува секоја научна публикација да има главен автор. Со тоа се зачувува академскиот интегритет на податоците и се спречува внесување нецелосни публикации. |
| | 465 | |
| | 466 | |
| | 467 | |
| | 468 | === Тригер 4: Автоматско поставување статус на фрагмент (trg_fragment_auto_status) === |
| | 469 | |
| | 470 | 1. **Намена:** |
| | 471 | Тригерот `trg_fragment_auto_status` е наменет за автоматско поставување статус на нов фрагмент при негово внесување во табелата `Fragments`. |
| | 472 | |
| | 473 | 2. **Случај на употреба:** |
| | 474 | Кога се внесува нов фрагмент кој е поврзан со конкретен археолошки предмет преку `object_id`, системот автоматски му поставува иницијален статус. |
| | 475 | |
| | 476 | Ова е корисно затоа што при теренска работа или масовно внесување податоци може да се внесат голем број фрагменти, па рачното поставување статус за секој од нив би било подложно на грешки. |
| | 477 | |
| | 478 | 3. **Причина за избор на тригер:** |
| | 479 | Тригерот е избран затоа што статусот треба автоматски да се постави во моментот на внесување, врз основа на вредноста на `object_id`. |
| | 480 | |
| | 481 | Ова правило е динамично: ако фрагментот е поврзан со предмет, тогаш добива статус. Затоа е соодветно да се користи `BEFORE INSERT` тригер, кој може да ја промени вредноста на `NEW.status_id` пред записот да биде зачуван. |
| | 482 | |
| | 483 | 4. **Имплементација:** |
| | 484 | |
| | 485 | Тригер функцијата е: |
| | 486 | |
| | 487 | {{{ |
| | 488 | CREATE OR REPLACE FUNCTION trg_auto_fragment_status() |
| | 489 | RETURNS TRIGGER AS $$ |
| | 490 | BEGIN |
| | 491 | IF NEW.object_id IS NOT NULL THEN |
| | 492 | NEW.status_id := 1; |
| | 493 | END IF; |
| | 494 | |
| | 495 | RETURN NEW; |
| | 496 | END; |
| | 497 | $$ LANGUAGE plpgsql; |
| | 498 | }}} |
| | 499 | |
| | 500 | Тригерот се активира: |
| | 501 | |
| | 502 | {{{ |
| | 503 | CREATE TRIGGER trg_fragment_auto_status |
| | 504 | BEFORE INSERT ON Fragments |
| | 505 | FOR EACH ROW |
| | 506 | EXECUTE FUNCTION trg_auto_fragment_status(); |
| | 507 | }}} |
| | 508 | |
| | 509 | 5. **Логика на работа:** |
| | 510 | Тригерот се активира пред секој внес во `Fragments`. |
| | 511 | |
| | 512 | Ако новиот фрагмент има вредност во `object_id`, тогаш автоматски се поставува: |
| | 513 | |
| | 514 | {{{ |
| | 515 | NEW.status_id := 1; |
| | 516 | }}} |
| | 517 | |
| | 518 | Со тоа фрагментот добива иницијален статус без корисникот да мора рачно да го внесе. |
| | 519 | |
| | 520 | 6. **Бизнис логика:** |
| | 521 | Овој тригер ја имплементира автоматизацијата на теренската и лабораториската евиденција на фрагменти. Во систем за културно наследство, фрагментите често се внесуваат во голем број, особено при археолошки ископувања или лабораториска обработка. |
| | 522 | |
| | 523 | Автоматското поставување статус обезбедува секој фрагмент кој е поврзан со предмет да има валиден почетен статус, што го олеснува понатамошното следење, класификација и конзервација. |
| | 524 | |
| | 525 | 7. **Заклучок:** |
| | 526 | `trg_fragment_auto_status` ја намалува можноста за човечка грешка при внесување фрагменти и обезбедува поголема конзистентност во базата. Тој е особено корисен при масовно внесување археолошки материјал. |
| | 527 | |
| | 528 | |
| | 529 | |
| | 530 | === Тригер 5: Проверка на совпаѓање помеѓу проект и предмет кај истражувачки пристап (trg_researcher_access_project_match) === |
| | 531 | |
| | 532 | 1. **Намена:** |
| | 533 | Тригерот `trg_researcher_access_project_match` е наменет за проверка дали конзерваторскиот проект наведен во `Researcher_Access` навистина се однесува на истиот предмет за кој се бара истражувачки пристап. |
| | 534 | |
| | 535 | 2. **Случај на употреба:** |
| | 536 | Во системот, истражувачкиот пристап може да биде поврзан со конкретен конзерваторски проект преку `conservation_project_id`. Секој конзерваторски проект во табелата `Conservation_Projects` е поврзан со точно одреден предмет преку `object_id`. |
| | 537 | |
| | 538 | Проблемот се јавува ако истражувач побара пристап до еден предмет, но во барањето наведе конзерваторски проект кој реално припаѓа на друг предмет. Таков запис би бил логички неконзистентен и би можел да доведе до погрешно одобрување на пристап. |
| | 539 | |
| | 540 | 3. **Причина за избор на тригер:** |
| | 541 | Овој тригер е избран затоа што правилото бара споредба на вредности од две различни табели: |
| | 542 | |
| | 543 | * `Researcher_Access.object_id` |
| | 544 | * `Conservation_Projects.object_id` |
| | 545 | |
| | 546 | Обичен `FOREIGN KEY` може да провери дали `conservation_project_id` постои во `Conservation_Projects`, но не може сам да провери дали тој проект се однесува на истиот `object_id` кој е наведен во барањето за пристап. |
| | 547 | |
| | 548 | Затоа се користи тригер, бидејќи тој може да изврши дополнителен `SELECT`, да ја прочита вредноста `object_id` од проектот и да ја спореди со новиот запис во `Researcher_Access`. |
| | 549 | |
| | 550 | 4. **Имплементација:** |
| | 551 | |
| | 552 | Тригер функцијата е: |
| | 553 | |
| | 554 | {{{ |
| | 555 | CREATE OR REPLACE FUNCTION trg_validate_project_object_match() |
| | 556 | RETURNS TRIGGER AS $$ |
| | 557 | DECLARE |
| | 558 | v_project_object_id BIGINT; |
| | 559 | BEGIN |
| | 560 | IF NEW.conservation_project_id IS NULL THEN |
| | 561 | RETURN NEW; |
| | 562 | END IF; |
| | 563 | |
| | 564 | SELECT object_id |
| | 565 | INTO v_project_object_id |
| | 566 | FROM Conservation_Projects |
| | 567 | WHERE project_id = NEW.conservation_project_id; |
| | 568 | |
| | 569 | IF v_project_object_id <> NEW.object_id THEN |
| | 570 | RAISE EXCEPTION 'Конзерваторскиот проект % не припаѓа на објектот %. Пристапот е одбиен.', |
| | 571 | NEW.conservation_project_id, NEW.object_id; |
| | 572 | END IF; |
| | 573 | |
| | 574 | RETURN NEW; |
| | 575 | END; |
| | 576 | $$ LANGUAGE plpgsql; |
| | 577 | }}} |
| | 578 | |
| | 579 | Тригерот се активира: |
| | 580 | |
| | 581 | {{{ |
| | 582 | CREATE TRIGGER trg_researcher_access_project_match |
| | 583 | BEFORE INSERT OR UPDATE ON Researcher_Access |
| | 584 | FOR EACH ROW |
| | 585 | EXECUTE FUNCTION trg_validate_project_object_match(); |
| | 586 | }}} |
| | 587 | |
| | 588 | 5. **Логика на работа:** |
| | 589 | Тригерот се активира пред секој `INSERT` или `UPDATE` во `Researcher_Access`. |
| | 590 | |
| | 591 | Прво проверува дали `NEW.conservation_project_id` е `NULL`. |
| | 592 | |
| | 593 | Ако нема поврзан проект, нема што да се проверува и записот продолжува. |
| | 594 | |
| | 595 | Ако постои поврзан проект, тригерот го пронаоѓа `object_id` од табелата `Conservation_Projects` за тој проект: |
| | 596 | |
| | 597 | {{{ |
| | 598 | SELECT object_id |
| | 599 | INTO v_project_object_id |
| | 600 | FROM Conservation_Projects |
| | 601 | WHERE project_id = NEW.conservation_project_id; |
| | 602 | }}} |
| | 603 | |
| | 604 | Потоа го споредува тој `object_id` со `NEW.object_id`. |
| | 605 | |
| | 606 | Ако вредностите не се исти, записот се одбива. |
| | 607 | |
| | 608 | 6. **Бизнис логика:** |
| | 609 | Овој тригер имплементира едно од најважните правила за контрола на истражувачки пристап. Во системот, пристапот не смее да се одобри преку проект кој не се однесува на истиот предмет. |
| | 610 | |
| | 611 | На пример, ако конзерваторски проект е креиран за предмет А, истражувачот не смее да го користи тој проект како основа за пристап до предмет Б. Тоа би создало сериозна неконзистентност во системот и би можело да доведе до неовластен пристап до погрешен артефакт. |
| | 612 | |
| | 613 | Ова е особено важно кај културното наследство, бидејќи предметите можат да имаат различен степен на заштита, различни истражувачки ограничувања и различна институционална сопственост. |
| | 614 | |
| | 615 | 7. **Зошто не е доволен FOREIGN KEY:** |
| | 616 | Стандардниот `FOREIGN KEY` constraint може да провери само дали вредноста `conservation_project_id` постои како `project_id` во `Conservation_Projects`. |
| | 617 | |
| | 618 | Но правилото овде е посложено. Треба да се провери дали: |
| | 619 | |
| | 620 | {{{ |
| | 621 | Researcher_Access.conservation_project_id -> Conservation_Projects.project_id |
| | 622 | }}} |
| | 623 | |
| | 624 | и истовремено дали: |
| | 625 | |
| | 626 | {{{ |
| | 627 | Researcher_Access.object_id = Conservation_Projects.object_id |
| | 628 | }}} |
| | 629 | |
| | 630 | Ова е меѓутабеларна логичка проверка и затоа е соодветно имплементирана со тригер. |
| | 631 | |
| | 632 | 8. **Заклучок:** |
| | 633 | `trg_researcher_access_project_match` обезбедува логичка конзистентност помеѓу истражувачките барања, предметите и конзерваторските проекти. Овој тригер спречува сериозни грешки во пристапот и ја зајакнува безбедноста и точноста на системот. |