| 5 | | Како напредна тема во рамки на проектот '''SCS - Smart City Security''' е имплементирана локална симулација на пропагација на податоци помеѓу централна и локална база на податоци. |
| 6 | | |
| 7 | | Идејата на оваа имплементација е да се симулира реален Smart City систем во кој постои една централна база, на ниво на град или институција, и една локална база, која може да припаѓа на полициска станица, локална зона или друг организациски дел од системот. |
| 8 | | |
| 9 | | Поради ограничување да не се прават дополнителни измени на факултетската база, пропагацијата е имплементирана локално преку две PostgreSQL бази: |
| 10 | | |
| 11 | | * '''scs_central''' - централна база |
| 12 | | * '''scs_local''' - локална база |
| 13 | | |
| 14 | | Централната база ги прима новите податоци, додека локалната база ги добива податоците преку механизам за пропагација. |
| | 5 | Како напредна тема во рамки на проектот '''SCS - Smart City Security''' е имплементиран механизам за пропагација на податоци помеѓу централна и локална база на податоци. |
| | 6 | |
| | 7 | Идејата на оваа имплементација е да се симулира Smart City Security систем во кој постои централна база, која ги содржи главните податоци за системот, и локална база, која добива копија од релевантните податоци. |
| | 8 | |
| | 9 | Во ваков систем централната база може да се користи на ниво на град или институција, додека локалната база може да припаѓа на одредена полициска станица, зона или организациски дел од системот. |
| | 10 | |
| | 11 | Целта на пропагацијата е при внесување нови податоци во централната база, тие контролирано да се префрлат и во локалната база. |
| 43 | | |
| 44 | | Статусот може да биде: |
| | 40 | * датум на креирање |
| | 41 | * датум на обработка |
| | 42 | * порака за грешка, доколку пропагацијата не успее |
| | 43 | |
| | 44 | {{{ |
| | 45 | CREATE TABLE propagation_log |
| | 46 | ( |
| | 47 | log_id SERIAL PRIMARY KEY, |
| | 48 | tabela VARCHAR(50) NOT NULL, |
| | 49 | operacija VARCHAR(20) NOT NULL, |
| | 50 | zapis_id VARCHAR(50) NOT NULL, |
| | 51 | payload JSONB NOT NULL, |
| | 52 | status VARCHAR(20) DEFAULT 'PENDING', |
| | 53 | datum_kreiranje TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
| | 54 | datum_obrabotka TIMESTAMP, |
| | 55 | poraka_greska TEXT |
| | 56 | ); |
| | 57 | }}} |
| | 58 | |
| | 59 | Статусот може да има една од следните вредности: |
| | 64 | |
| | 65 | == Функција за логирање на промени == |
| | 66 | |
| | 67 | За евидентирање на новите записи е креирана функцијата: |
| | 68 | |
| | 69 | {{{ |
| | 70 | fn_log_propagation_insert() |
| | 71 | }}} |
| | 72 | |
| | 73 | Оваа функција се повикува преку trigger-и. Таа го зема новиот внесен ред преку '''NEW''', го претвора во JSONB формат и го запишува во '''propagation_log'''. |
| | 74 | |
| | 75 | {{{ |
| | 76 | CREATE OR REPLACE FUNCTION fn_log_propagation_insert() |
| | 77 | RETURNS TRIGGER AS $$ |
| | 78 | DECLARE |
| | 79 | pk_column TEXT; |
| | 80 | pk_value TEXT; |
| | 81 | BEGIN |
| | 82 | pk_column := TG_ARGV[0]; |
| | 83 | pk_value := to_jsonb(NEW) ->> pk_column; |
| | 84 | |
| | 85 | ``` |
| | 86 | INSERT INTO propagation_log ( |
| | 87 | tabela, |
| | 88 | operacija, |
| | 89 | zapis_id, |
| | 90 | payload |
| | 91 | ) |
| | 92 | VALUES ( |
| | 93 | TG_TABLE_NAME, |
| | 94 | 'INSERT', |
| | 95 | pk_value, |
| | 96 | to_jsonb(NEW) |
| | 97 | ); |
| | 98 | |
| | 99 | RETURN NEW; |
| | 100 | ``` |
| | 101 | |
| | 102 | END; |
| | 103 | $$ LANGUAGE plpgsql; |
| | 104 | }}} |
| | 105 | |
| | 106 | Функцијата како аргумент го добива името на primary key колоната за табелата на која се однесува trigger-от. На тој начин истата функција може да се користи за повеќе табели. |
| | 107 | |
| | 108 | Користењето на JSONB овозможува целиот запис да се зачува како payload, без потреба да се креира посебна log табела за секоја табела. |
| 62 | | Со ова се овозможува асинхрона пропагација, бидејќи податоците прво се евидентираат, а потоа се обработуваат со посебна процедура. |
| 63 | | |
| 64 | | == Функција за логирање на промени == |
| 65 | | |
| 66 | | За trigger-ите е креирана функцијата: |
| 67 | | |
| 68 | | {{{ |
| 69 | | fn_log_propagation_insert() |
| 70 | | }}} |
| 71 | | |
| 72 | | Оваа функција го зема новиот внесен ред преку '''NEW''', го претвора во JSONB формат и го запишува во '''propagation_log'''. |
| 73 | | |
| 74 | | Користењето на JSONB овозможува целиот запис да се зачува како payload, без потреба да се креира посебна log табела за секоја табела од системот. |
| | 122 | {{{ |
| | 123 | DROP TRIGGER IF EXISTS trg_log_gragjanin_insert ON gragjanin; |
| | 124 | DROP TRIGGER IF EXISTS trg_log_vozilo_insert ON vozilo; |
| | 125 | DROP TRIGGER IF EXISTS trg_log_kamera_insert ON kamera; |
| | 126 | DROP TRIGGER IF EXISTS trg_log_prekrsok_insert ON prekrsok; |
| | 127 | DROP TRIGGER IF EXISTS trg_log_kazna_insert ON kazna; |
| | 128 | |
| | 129 | CREATE TRIGGER trg_log_gragjanin_insert |
| | 130 | AFTER INSERT ON gragjanin |
| | 131 | FOR EACH ROW |
| | 132 | EXECUTE FUNCTION fn_log_propagation_insert('embg'); |
| | 133 | |
| | 134 | CREATE TRIGGER trg_log_vozilo_insert |
| | 135 | AFTER INSERT ON vozilo |
| | 136 | FOR EACH ROW |
| | 137 | EXECUTE FUNCTION fn_log_propagation_insert('registarska_oznaka'); |
| | 138 | |
| | 139 | CREATE TRIGGER trg_log_kamera_insert |
| | 140 | AFTER INSERT ON kamera |
| | 141 | FOR EACH ROW |
| | 142 | EXECUTE FUNCTION fn_log_propagation_insert('kamera_id'); |
| | 143 | |
| | 144 | CREATE TRIGGER trg_log_prekrsok_insert |
| | 145 | AFTER INSERT ON prekrsok |
| | 146 | FOR EACH ROW |
| | 147 | EXECUTE FUNCTION fn_log_propagation_insert('prekrsok_id'); |
| | 148 | |
| | 149 | CREATE TRIGGER trg_log_kazna_insert |
| | 150 | AFTER INSERT ON kazna |
| | 151 | FOR EACH ROW |
| | 152 | EXECUTE FUNCTION fn_log_propagation_insert('kazna_id'); |
| | 153 | }}} |
| | 154 | |
| | 155 | Со ова при секој нов INSERT во некоја од наведените табели автоматски се креира запис во '''propagation_log'''. |
| | 169 | {{{ |
| | 170 | CREATE OR REPLACE PROCEDURE sp_propagiraj_vo_lokalna_baza() |
| | 171 | LANGUAGE plpgsql |
| | 172 | AS $$ |
| | 173 | DECLARE |
| | 174 | r RECORD; |
| | 175 | p JSONB; |
| | 176 | conn TEXT := 'host=127.0.0.1 port=5430 dbname=scs_local user=postgres password=postgres'; |
| | 177 | BEGIN |
| | 178 | FOR r IN |
| | 179 | SELECT * |
| | 180 | FROM propagation_log |
| | 181 | WHERE status = 'PENDING' |
| | 182 | ORDER BY log_id |
| | 183 | LOOP |
| | 184 | BEGIN |
| | 185 | p := r.payload; |
| | 186 | |
| | 187 | ``` |
| | 188 | IF r.tabela = 'gragjanin' THEN |
| | 189 | |
| | 190 | PERFORM dblink_exec( |
| | 191 | conn, |
| | 192 | format( |
| | 193 | 'INSERT INTO gragjanin |
| | 194 | (embg, ime, prezime, adresa, grad, telefonski_broj, datum_ragjanje, pol) |
| | 195 | VALUES (%L, %L, %L, %L, %L, %L, %L, %L) |
| | 196 | ON CONFLICT (embg) DO NOTHING', |
| | 197 | p ->> 'embg', |
| | 198 | p ->> 'ime', |
| | 199 | p ->> 'prezime', |
| | 200 | p ->> 'adresa', |
| | 201 | p ->> 'grad', |
| | 202 | p ->> 'telefonski_broj', |
| | 203 | p ->> 'datum_ragjanje', |
| | 204 | p ->> 'pol' |
| | 205 | ) |
| | 206 | ); |
| | 207 | |
| | 208 | ELSIF r.tabela = 'vozilo' THEN |
| | 209 | |
| | 210 | PERFORM dblink_exec( |
| | 211 | conn, |
| | 212 | format( |
| | 213 | 'INSERT INTO vozilo |
| | 214 | (registarska_oznaka, marka, model, boja, embg_sopstvenik) |
| | 215 | VALUES (%L, %L, %L, %L, %L) |
| | 216 | ON CONFLICT (registarska_oznaka) DO NOTHING', |
| | 217 | p ->> 'registarska_oznaka', |
| | 218 | p ->> 'marka', |
| | 219 | p ->> 'model', |
| | 220 | p ->> 'boja', |
| | 221 | p ->> 'embg_sopstvenik' |
| | 222 | ) |
| | 223 | ); |
| | 224 | |
| | 225 | ELSIF r.tabela = 'kamera' THEN |
| | 226 | |
| | 227 | PERFORM dblink_exec( |
| | 228 | conn, |
| | 229 | format( |
| | 230 | 'INSERT INTO kamera |
| | 231 | (kamera_id, lokacija, aktivna) |
| | 232 | VALUES (%L, %L, %L) |
| | 233 | ON CONFLICT (kamera_id) DO NOTHING', |
| | 234 | p ->> 'kamera_id', |
| | 235 | p ->> 'lokacija', |
| | 236 | p ->> 'aktivna' |
| | 237 | ) |
| | 238 | ); |
| | 239 | |
| | 240 | ELSIF r.tabela = 'prekrsok' THEN |
| | 241 | |
| | 242 | PERFORM dblink_exec( |
| | 243 | conn, |
| | 244 | format( |
| | 245 | 'INSERT INTO prekrsok |
| | 246 | (prekrsok_id, datum, opis, kamera_id, registarska_oznaka, embg_storitel) |
| | 247 | VALUES (%L, %L, %L, %L, %L, %L) |
| | 248 | ON CONFLICT (prekrsok_id) DO NOTHING', |
| | 249 | p ->> 'prekrsok_id', |
| | 250 | p ->> 'datum', |
| | 251 | p ->> 'opis', |
| | 252 | p ->> 'kamera_id', |
| | 253 | p ->> 'registarska_oznaka', |
| | 254 | p ->> 'embg_storitel' |
| | 255 | ) |
| | 256 | ); |
| | 257 | |
| | 258 | ELSIF r.tabela = 'kazna' THEN |
| | 259 | |
| | 260 | PERFORM dblink_exec( |
| | 261 | conn, |
| | 262 | format( |
| | 263 | 'INSERT INTO kazna |
| | 264 | (kazna_id, prekrsok_id, datum, iznos_za_plakanje, rok_na_plakanje, status) |
| | 265 | VALUES (%L, %L, %L, %L, %L, %L) |
| | 266 | ON CONFLICT (kazna_id) DO NOTHING', |
| | 267 | p ->> 'kazna_id', |
| | 268 | p ->> 'prekrsok_id', |
| | 269 | p ->> 'datum', |
| | 270 | p ->> 'iznos_za_plakanje', |
| | 271 | p ->> 'rok_na_plakanje', |
| | 272 | p ->> 'status' |
| | 273 | ) |
| | 274 | ); |
| | 275 | |
| | 276 | END IF; |
| | 277 | |
| | 278 | UPDATE propagation_log |
| | 279 | SET status = 'DONE', |
| | 280 | datum_obrabotka = CURRENT_TIMESTAMP, |
| | 281 | poraka_greska = NULL |
| | 282 | WHERE log_id = r.log_id; |
| | 283 | |
| | 284 | EXCEPTION WHEN OTHERS THEN |
| | 285 | UPDATE propagation_log |
| | 286 | SET status = 'FAILED', |
| | 287 | datum_obrabotka = CURRENT_TIMESTAMP, |
| | 288 | poraka_greska = SQLERRM |
| | 289 | WHERE log_id = r.log_id; |
| | 290 | END; |
| | 291 | END LOOP; |
| | 292 | ``` |
| | 293 | |
| | 294 | END; |
| | 295 | $$; |
| | 296 | }}} |
| | 297 | |