| 1 | -- функции, тригери, процедури
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 | -- тригер за додавање stream по додавање или ажурирање на сесија која траела подолго од 30 секунди
|
|---|
| 5 |
|
|---|
| 6 | CREATE OR REPLACE FUNCTION trg_record_stream_from_session()
|
|---|
| 7 | RETURNS TRIGGER AS $$
|
|---|
| 8 | BEGIN
|
|---|
| 9 | IF NEW.listened_ms >= 30000
|
|---|
| 10 | AND NOT EXISTS (SELECT 1
|
|---|
| 11 | FROM Song_Streams ss
|
|---|
| 12 | WHERE ss.playback_session_id = NEW.id)
|
|---|
| 13 | THEN
|
|---|
| 14 | INSERT INTO Song_Streams (playback_session_id, song_id, streamed_at, user_id)
|
|---|
| 15 | VALUES (NEW.id, NEW.song_id, NEW.started_at, NEW.user_id);
|
|---|
| 16 | END IF;
|
|---|
| 17 | RETURN NEW;
|
|---|
| 18 | END;
|
|---|
| 19 | $$ language plpgsql;
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 | DROP TRIGGER IF EXISTS record_stream_from_session on playback_sessions;
|
|---|
| 23 |
|
|---|
| 24 | CREATE TRIGGER record_stream_from_session
|
|---|
| 25 | AFTER INSERT OR UPDATE OF listened_ms ON playback_sessions
|
|---|
| 26 | FOR EACH ROW
|
|---|
| 27 | EXECUTE FUNCTION trg_record_stream_from_session();
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 | -- тригер за проверка дека една песна не може да има релација (ремикс, препев, ...) со самата себе
|
|---|
| 31 | -- и дека дека нема да има дупликати од истата релација
|
|---|
| 32 |
|
|---|
| 33 | CREATE OR REPLACE FUNCTION trg_check_song_relationship()
|
|---|
| 34 | RETURNS TRIGGER AS $$
|
|---|
| 35 | BEGIN
|
|---|
| 36 | IF NEW.source_song_id = NEW.target_song_id THEN
|
|---|
| 37 | RAISE EXCEPTION 'A song cannot have a relationship with itself (song %)',
|
|---|
| 38 | NEW.source_song_id;
|
|---|
| 39 | END IF;
|
|---|
| 40 |
|
|---|
| 41 | IF EXISTS (
|
|---|
| 42 | SELECT 1
|
|---|
| 43 | FROM Song_Relationships sr
|
|---|
| 44 | WHERE sr.source_song_id = NEW.source_song_id
|
|---|
| 45 | AND sr.target_song_id = NEW.target_song_id
|
|---|
| 46 | AND sr.relationship_type = NEW.relationship_type
|
|---|
| 47 | AND sr.id <> COALESCE(NEW.id, -1)
|
|---|
| 48 | ) THEN
|
|---|
| 49 | RAISE EXCEPTION 'Duplicate % relationship between songs % and %',
|
|---|
| 50 | NEW.relationship_type, NEW.source_song_id, NEW.target_song_id;
|
|---|
| 51 | END IF;
|
|---|
| 52 |
|
|---|
| 53 | RETURN NEW;
|
|---|
| 54 | END;
|
|---|
| 55 | $$ LANGUAGE plpgsql;
|
|---|
| 56 |
|
|---|
| 57 | DROP TRIGGER IF EXISTS check_song_relationship ON Song_Relationships;
|
|---|
| 58 | CREATE TRIGGER check_song_relationship
|
|---|
| 59 | BEFORE INSERT OR UPDATE ON Song_Relationships
|
|---|
| 60 | FOR EACH ROW
|
|---|
| 61 | EXECUTE FUNCTION trg_check_song_relationship();
|
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 | -- функција за броење на бројот на слушања на одредена песна во изминат период
|
|---|
| 65 |
|
|---|
| 66 | CREATE OR REPLACE FUNCTION song_stream_count(
|
|---|
| 67 | p_song_id BIGINT,
|
|---|
| 68 | p_window INTERVAL DEFAULT INTERVAL '30 days'
|
|---|
| 69 | ) RETURNS BIGINT AS $$
|
|---|
| 70 | SELECT COUNT(*)
|
|---|
| 71 | FROM Song_Streams ss
|
|---|
| 72 | WHERE ss.song_id = p_song_id
|
|---|
| 73 | AND ss.streamed_at >= CURRENT_TIMESTAMP - p_window;
|
|---|
| 74 | $$ LANGUAGE sql STABLE;
|
|---|
| 75 |
|
|---|
| 76 |
|
|---|
| 77 | -- функција за проверување дали даден корисник може да преземе одредена акција - главна авторизациска логика
|
|---|
| 78 |
|
|---|
| 79 | CREATE OR REPLACE FUNCTION can_user_perform(
|
|---|
| 80 | p_user_id BIGINT,
|
|---|
| 81 | p_action VARCHAR,
|
|---|
| 82 | p_resource_type VARCHAR,
|
|---|
| 83 | p_resource_id BIGINT
|
|---|
| 84 | ) RETURNS BOOLEAN AS $$
|
|---|
| 85 | DECLARE
|
|---|
| 86 | v_scopes TEXT[];
|
|---|
| 87 | v_is_owner BOOLEAN := FALSE;
|
|---|
| 88 | v_visibility TEXT;
|
|---|
| 89 | v_is_shared BOOLEAN := FALSE;
|
|---|
| 90 | BEGIN
|
|---|
| 91 | -- site scopes dodeleni na ovoj user za torkata (action, resource_type)
|
|---|
| 92 | SELECT ARRAY_AGG(DISTINCT p.scope)
|
|---|
| 93 | INTO v_scopes
|
|---|
| 94 | FROM User_Roles ur
|
|---|
| 95 | JOIN Role_Permissions rp ON rp.role_id = ur.role_id
|
|---|
| 96 | JOIN Permissions p ON p.id = rp.permission_id
|
|---|
| 97 | WHERE ur.user_id = p_user_id
|
|---|
| 98 | AND p.action = p_action
|
|---|
| 99 | AND p.resource_type = p_resource_type;
|
|---|
| 100 |
|
|---|
| 101 | IF v_scopes IS NULL THEN
|
|---|
| 102 | RETURN FALSE;
|
|---|
| 103 | END IF;
|
|---|
| 104 |
|
|---|
| 105 | -- opredeli ownership + visibility za sekoj resource_type
|
|---|
| 106 | IF p_resource_type = 'SONG' THEN
|
|---|
| 107 | SELECT s.visibility,
|
|---|
| 108 | EXISTS (SELECT 1 FROM Artists a
|
|---|
| 109 | WHERE a.id = s.owner_artist_id
|
|---|
| 110 | AND a.user_id = p_user_id)
|
|---|
| 111 | INTO v_visibility, v_is_owner
|
|---|
| 112 | FROM Songs s WHERE s.id = p_resource_id;
|
|---|
| 113 |
|
|---|
| 114 | ELSIF p_resource_type = 'ALBUM' THEN
|
|---|
| 115 | SELECT al.visibility,
|
|---|
| 116 | EXISTS (SELECT 1 FROM Artists a
|
|---|
| 117 | WHERE a.id = al.owner_artist_id
|
|---|
| 118 | AND a.user_id = p_user_id)
|
|---|
| 119 | INTO v_visibility, v_is_owner
|
|---|
| 120 | FROM Albums al WHERE al.id = p_resource_id;
|
|---|
| 121 |
|
|---|
| 122 | ELSIF p_resource_type = 'PLAYLIST' THEN
|
|---|
| 123 | SELECT pl.visibility,
|
|---|
| 124 | (pl.creator_user_id = p_user_id)
|
|---|
| 125 | INTO v_visibility, v_is_owner
|
|---|
| 126 | FROM Playlists pl WHERE pl.id = p_resource_id;
|
|---|
| 127 | END IF;
|
|---|
| 128 |
|
|---|
| 129 | -- resursot ne postoi / nepoznat resource_type
|
|---|
| 130 | IF v_visibility IS NULL THEN
|
|---|
| 131 | RETURN FALSE;
|
|---|
| 132 | END IF;
|
|---|
| 133 |
|
|---|
| 134 | -- ANY scope dozvoluva se, nema potreba od dopolnitelni proverki (dokolku resursot postoi)
|
|---|
| 135 | IF 'ANY' = ANY (v_scopes) THEN
|
|---|
| 136 | RETURN TRUE;
|
|---|
| 137 | END IF;
|
|---|
| 138 |
|
|---|
| 139 | IF v_is_owner AND 'OWN' = ANY (v_scopes) THEN
|
|---|
| 140 | RETURN TRUE;
|
|---|
| 141 | END IF;
|
|---|
| 142 |
|
|---|
| 143 | IF v_visibility = 'PUBLIC' AND 'PUBLIC' = ANY (v_scopes) THEN
|
|---|
| 144 | RETURN TRUE;
|
|---|
| 145 | END IF;
|
|---|
| 146 |
|
|---|
| 147 | -- SHARED: resursot moze da e spodelen direktno so korisnikot
|
|---|
| 148 | -- ILI so edna od roljite koja korisnikot gi poseduva, I samiot share dozvoluva p_action
|
|---|
| 149 | IF 'SHARED' = ANY (v_scopes) THEN
|
|---|
| 150 | SELECT EXISTS (
|
|---|
| 151 | SELECT 1
|
|---|
| 152 | FROM Resource_Shares rs
|
|---|
| 153 | JOIN Permissions sp ON sp.id = rs.permission_id
|
|---|
| 154 | WHERE (
|
|---|
| 155 | (p_resource_type = 'SONG' AND rs.song_id = p_resource_id) OR
|
|---|
| 156 | (p_resource_type = 'ALBUM' AND rs.album_id = p_resource_id) OR
|
|---|
| 157 | (p_resource_type = 'PLAYLIST' AND rs.playlist_id = p_resource_id)
|
|---|
| 158 | )
|
|---|
| 159 | AND sp.action = p_action
|
|---|
| 160 | AND sp.resource_type = p_resource_type
|
|---|
| 161 | AND (
|
|---|
| 162 | rs.user_id = p_user_id
|
|---|
| 163 | OR rs.role_id IN (SELECT ur.role_id
|
|---|
| 164 | FROM User_Roles ur
|
|---|
| 165 | WHERE ur.user_id = p_user_id)
|
|---|
| 166 | )
|
|---|
| 167 | ) INTO v_is_shared;
|
|---|
| 168 |
|
|---|
| 169 | IF v_is_shared THEN
|
|---|
| 170 | RETURN TRUE;
|
|---|
| 171 | END IF;
|
|---|
| 172 | END IF;
|
|---|
| 173 |
|
|---|
| 174 | RETURN FALSE;
|
|---|
| 175 | END;
|
|---|
| 176 | $$ LANGUAGE plpgsql STABLE;
|
|---|
| 177 |
|
|---|
| 178 |
|
|---|
| 179 |
|
|---|
| 180 |
|
|---|
| 181 | -- тригер за проверка дали артистот и админот се дел од истата издавачка куќа при
|
|---|
| 182 | -- објавување на песна/албум
|
|---|
| 183 |
|
|---|
| 184 | CREATE OR REPLACE FUNCTION check_same_label()
|
|---|
| 185 | RETURNS TRIGGER
|
|---|
| 186 | LANGUAGE plpgsql
|
|---|
| 187 | AS $$
|
|---|
| 188 | BEGIN
|
|---|
| 189 | IF NEW.published_by_label_id IS NOT NULL THEN
|
|---|
| 190 | IF NOT EXISTS (
|
|---|
| 191 | SELECT 1
|
|---|
| 192 | FROM artist_labels al
|
|---|
| 193 | WHERE al.label_id = NEW.published_by_label_id
|
|---|
| 194 | AND al.artist_id = NEW.owner_artist_id
|
|---|
| 195 | AND al.active = TRUE
|
|---|
| 196 | ) THEN
|
|---|
| 197 | RAISE EXCEPTION 'Artist does not belong to this label';
|
|---|
| 198 | END IF;
|
|---|
| 199 | END IF;
|
|---|
| 200 |
|
|---|
| 201 | RETURN NEW;
|
|---|
| 202 | END;
|
|---|
| 203 | $$;
|
|---|
| 204 |
|
|---|
| 205 |
|
|---|
| 206 | DROP TRIGGER IF EXISTS check_same_admin_artist_label ON songs;
|
|---|
| 207 | DROP TRIGGER IF EXISTS check_same_admin_artist_label ON albums;
|
|---|
| 208 |
|
|---|
| 209 | CREATE OR REPLACE TRIGGER check_same_admin_artist_label
|
|---|
| 210 | BEFORE INSERT OR UPDATE ON songs
|
|---|
| 211 | FOR EACH ROW
|
|---|
| 212 | EXECUTE FUNCTION check_same_label();
|
|---|
| 213 |
|
|---|
| 214 |
|
|---|
| 215 | CREATE OR REPLACE TRIGGER check_same_admin_artist_label
|
|---|
| 216 | BEFORE INSERT OR UPDATE ON albums
|
|---|
| 217 | FOR EACH ROW
|
|---|
| 218 | EXECUTE FUNCTION check_same_label();
|
|---|
| 219 |
|
|---|
| 220 |
|
|---|
| 221 |
|
|---|
| 222 | -- тригер за автоматско ажурирање на припадноста на артистот кон издавачка куќа при негово префрлање во нова издавачка куќа
|
|---|
| 223 |
|
|---|
| 224 | CREATE OR REPLACE FUNCTION handle_new_artist_in_label()
|
|---|
| 225 | RETURNS TRIGGER
|
|---|
| 226 | LANGUAGE plpgsql
|
|---|
| 227 | AS $$
|
|---|
| 228 | BEGIN
|
|---|
| 229 | UPDATE artist_labels
|
|---|
| 230 | SET end_date = NOW(),
|
|---|
| 231 | active = FALSE
|
|---|
| 232 | WHERE artist_id = NEW.artist_id
|
|---|
| 233 | AND active = TRUE;
|
|---|
| 234 |
|
|---|
| 235 | NEW.active := TRUE;
|
|---|
| 236 | NEW.start_date := NOW();
|
|---|
| 237 |
|
|---|
| 238 | RETURN NEW;
|
|---|
| 239 | END;
|
|---|
| 240 | $$;
|
|---|
| 241 |
|
|---|
| 242 | DROP TRIGGER IF EXISTS handle_new_artist_in_label ON artist_labels;
|
|---|
| 243 |
|
|---|
| 244 | CREATE TRIGGER handle_new_artist_in_label
|
|---|
| 245 | BEFORE INSERT ON artist_labels
|
|---|
| 246 | FOR EACH ROW
|
|---|
| 247 | EXECUTE FUNCTION handle_new_artist_in_label();
|
|---|
| 248 |
|
|---|
| 249 |
|
|---|
| 250 | -- процедура за објавување на албум со песни
|
|---|
| 251 |
|
|---|
| 252 | CREATE OR REPLACE PROCEDURE upload_album_with_songs(
|
|---|
| 253 | p_album_title VARCHAR,
|
|---|
| 254 | p_album_visibility VARCHAR,
|
|---|
| 255 | p_owner_artist_id BIGINT,
|
|---|
| 256 | p_published_by_artist_id BIGINT,
|
|---|
| 257 | p_published_by_label_id BIGINT,
|
|---|
| 258 | p_song_ids BIGINT[],
|
|---|
| 259 | INOUT p_album_id BIGINT DEFAULT NULL
|
|---|
| 260 | )
|
|---|
| 261 | LANGUAGE plpgsql
|
|---|
| 262 | AS $$
|
|---|
| 263 | BEGIN
|
|---|
| 264 | IF p_song_ids IS NULL
|
|---|
| 265 | OR array_length(p_song_ids, 1) IS NULL THEN
|
|---|
| 266 | RAISE EXCEPTION 'Album must contain at least one song';
|
|---|
| 267 | END IF;
|
|---|
| 268 |
|
|---|
| 269 | IF EXISTS (
|
|---|
| 270 | SELECT 1
|
|---|
| 271 | FROM albums
|
|---|
| 272 | WHERE title = p_album_title
|
|---|
| 273 | ) THEN
|
|---|
| 274 | RAISE EXCEPTION 'Album with that title already exists';
|
|---|
| 275 | END IF;
|
|---|
| 276 |
|
|---|
| 277 | INSERT INTO albums (
|
|---|
| 278 | title,
|
|---|
| 279 | visibility,
|
|---|
| 280 | owner_artist_id,
|
|---|
| 281 | published_by_artist_id,
|
|---|
| 282 | published_by_label_id
|
|---|
| 283 | )
|
|---|
| 284 | VALUES (
|
|---|
| 285 | p_album_title,
|
|---|
| 286 | p_album_visibility,
|
|---|
| 287 | p_owner_artist_id,
|
|---|
| 288 | p_published_by_artist_id,
|
|---|
| 289 | p_published_by_label_id
|
|---|
| 290 | )
|
|---|
| 291 | RETURNING id INTO p_album_id;
|
|---|
| 292 |
|
|---|
| 293 | INSERT INTO album_tracks (
|
|---|
| 294 | album_id,
|
|---|
| 295 | song_id,
|
|---|
| 296 | track_number
|
|---|
| 297 | )
|
|---|
| 298 | SELECT
|
|---|
| 299 | p_album_id,
|
|---|
| 300 | song_id,
|
|---|
| 301 | ordinality
|
|---|
| 302 | FROM unnest(p_song_ids)
|
|---|
| 303 | WITH ORDINALITY AS t(song_id, ordinality);
|
|---|
| 304 | END;
|
|---|
| 305 | $$;
|
|---|
| 306 |
|
|---|
| 307 |
|
|---|
| 308 |
|
|---|
| 309 | -- процедура за ажурирање на сите материјализирани погледи
|
|---|
| 310 |
|
|---|
| 311 | CREATE OR REPLACE PROCEDURE refresh_all_materialized_views()
|
|---|
| 312 | LANGUAGE plpgsql
|
|---|
| 313 | AS $$
|
|---|
| 314 | DECLARE
|
|---|
| 315 | v_mv RECORD;
|
|---|
| 316 | BEGIN
|
|---|
| 317 | FOR v_mv IN
|
|---|
| 318 | SELECT schemaname,
|
|---|
| 319 | matviewname
|
|---|
| 320 | FROM pg_matviews
|
|---|
| 321 | WHERE schemaname = 'public'
|
|---|
| 322 | LOOP
|
|---|
| 323 | RAISE NOTICE 'Refreshing %.%',
|
|---|
| 324 | v_mv.schemaname,
|
|---|
| 325 | v_mv.matviewname;
|
|---|
| 326 |
|
|---|
| 327 | EXECUTE format(
|
|---|
| 328 | 'REFRESH MATERIALIZED VIEW %I.%I',
|
|---|
| 329 | v_mv.schemaname,
|
|---|
| 330 | v_mv.matviewname
|
|---|
| 331 | );
|
|---|
| 332 | END LOOP;
|
|---|
| 333 | END;
|
|---|
| 334 | $$;
|
|---|
| 335 |
|
|---|
| 336 |
|
|---|
| 337 | -- функција за агрегирање на податоци од изминати song_streams партиции во song_stream_counts_archive
|
|---|
| 338 | -- и маркирање на тие месеци како затворени (податоците се користат во view #7)
|
|---|
| 339 |
|
|---|
| 340 | -- помошни табели
|
|---|
| 341 |
|
|---|
| 342 | CREATE TABLE IF NOT EXISTS song_stream_sealed_partitions (
|
|---|
| 343 | partition_month date PRIMARY KEY, -- prviot den od zatvoreniot mesecot
|
|---|
| 344 | sealed_at timestamptz NOT NULL DEFAULT now()
|
|---|
| 345 | );
|
|---|
| 346 |
|
|---|
| 347 | -- kumulativni brojaci po pesna za site do sega zatvoreni meseci
|
|---|
| 348 | CREATE TABLE IF NOT EXISTS song_stream_counts_archive (
|
|---|
| 349 | song_id bigint PRIMARY KEY,
|
|---|
| 350 | streams bigint NOT NULL
|
|---|
| 351 | );
|
|---|
| 352 |
|
|---|
| 353 | CREATE OR REPLACE FUNCTION seal_closed_song_streams_months()
|
|---|
| 354 | RETURNS void
|
|---|
| 355 | LANGUAGE plpgsql
|
|---|
| 356 | AS $$
|
|---|
| 357 | DECLARE
|
|---|
| 358 | current_month date := date_trunc('month', current_date)::date;
|
|---|
| 359 | r record;
|
|---|
| 360 | yr int;
|
|---|
| 361 | mo int;
|
|---|
| 362 | part_month date;
|
|---|
| 363 | BEGIN
|
|---|
| 364 | FOR r IN -- iterirame niz sekoja particija
|
|---|
| 365 | SELECT c.relname
|
|---|
| 366 | FROM pg_inherits i
|
|---|
| 367 | JOIN pg_class c ON c.oid = i.inhrelid
|
|---|
| 368 | JOIN pg_class p ON p.oid = i.inhparent
|
|---|
| 369 | WHERE p.relname = 'song_streams'
|
|---|
| 370 | AND c.relname ~ '^song_streams_y\d{4}m\d{2}$' -- isklucuva default particija
|
|---|
| 371 | ORDER BY c.relname -- pocnuva od najstarata
|
|---|
| 372 | LOOP
|
|---|
| 373 | yr := substring(r.relname from 'y(\d{4})m')::int;
|
|---|
| 374 | mo := substring(r.relname from 'm(\d{2})$')::int;
|
|---|
| 375 | part_month := make_date(yr, mo, 1);
|
|---|
| 376 |
|
|---|
| 377 | -- zatvarame (seal-nuvame) samo meseci koi se celosno pominati i ne se zatvoreni do sega
|
|---|
| 378 | CONTINUE WHEN part_month >= current_month;
|
|---|
| 379 | CONTINUE WHEN EXISTS (
|
|---|
| 380 | SELECT 1 FROM song_stream_sealed_partitions s
|
|---|
| 381 | WHERE s.partition_month = part_month
|
|---|
| 382 | );
|
|---|
| 383 |
|
|---|
| 384 | -- generira per-song brojaci za segasniot mesec i gi vnesuva so stream count arhivata
|
|---|
| 385 | -- koristenjeto na particiite tuka e eksplicitno (r e imeto na particioniranata tabela)
|
|---|
| 386 | EXECUTE format(
|
|---|
| 387 | 'INSERT INTO song_stream_counts_archive AS a (song_id, streams) '
|
|---|
| 388 | 'SELECT song_id, count(*) FROM %I GROUP BY song_id '
|
|---|
| 389 | 'ON CONFLICT (song_id) DO UPDATE SET streams = a.streams + EXCLUDED.streams',
|
|---|
| 390 | r.relname
|
|---|
| 391 | );
|
|---|
| 392 |
|
|---|
| 393 | -- markiraj go kako sealed
|
|---|
| 394 | INSERT INTO song_stream_sealed_partitions (partition_month)
|
|---|
| 395 | VALUES (part_month);
|
|---|
| 396 |
|
|---|
| 397 | RAISE NOTICE 'sealed month %', to_char(part_month, 'YYYY-MM');
|
|---|
| 398 | END LOOP;
|
|---|
| 399 | END;
|
|---|
| 400 | $$;
|
|---|
| 401 |
|
|---|
| 402 |
|
|---|
| 403 | -- функции за овозможување стриминг на песни
|
|---|
| 404 |
|
|---|
| 405 | -- помошна функција за земање големина на песната во бајти.
|
|---|
| 406 | -- backend-от ја користи за да го пресмета Content-Range header-от и да валидира опсези
|
|---|
| 407 | CREATE OR REPLACE FUNCTION song_content_size(p_song_id bigint)
|
|---|
| 408 | RETURNS bigint
|
|---|
| 409 | LANGUAGE sql
|
|---|
| 410 | STABLE
|
|---|
| 411 | AS $$
|
|---|
| 412 | SELECT octet_length(content)
|
|---|
| 413 | FROM song_contents
|
|---|
| 414 | WHERE song_id = p_song_id
|
|---|
| 415 | LIMIT 1;
|
|---|
| 416 | $$;
|
|---|
| 417 |
|
|---|
| 418 |
|
|---|
| 419 | -- функција која враќа сегмент бајти од песната - главната (core) функција корисна за самиот стриминг
|
|---|
| 420 | -- p_start - 0-indexed offset (како во HTTP Range), inclusive
|
|---|
| 421 | -- p_length - колку бајти да се вратат од таа позиција
|
|---|
| 422 |
|
|---|
| 423 | CREATE OR REPLACE FUNCTION song_content_chunk(
|
|---|
| 424 | p_song_id bigint,
|
|---|
| 425 | p_start bigint,
|
|---|
| 426 | p_length integer
|
|---|
| 427 | )
|
|---|
| 428 | RETURNS bytea
|
|---|
| 429 | LANGUAGE sql
|
|---|
| 430 | STABLE
|
|---|
| 431 | AS $$
|
|---|
| 432 | SELECT substring(content FROM (p_start + 1)::int FOR p_length) -- substring() е 1-indexed па додаваме +1
|
|---|
| 433 | FROM song_contents
|
|---|
| 434 | WHERE song_id = p_song_id
|
|---|
| 435 | LIMIT 1;
|
|---|
| 436 | $$;
|
|---|
| 437 |
|
|---|
| 438 |
|
|---|
| 439 | -- функции за менаџирање на сесии (playback_session)
|
|---|
| 440 |
|
|---|
| 441 | -- функција за започнување нова сесија (listened_ms = 0). враќа id-то на сесијата.
|
|---|
| 442 |
|
|---|
| 443 | CREATE OR REPLACE FUNCTION start_playback_session(
|
|---|
| 444 | p_user_id bigint,
|
|---|
| 445 | p_song_id bigint
|
|---|
| 446 | )
|
|---|
| 447 | RETURNS bigint
|
|---|
| 448 | LANGUAGE sql
|
|---|
| 449 | AS $$
|
|---|
| 450 | INSERT INTO playback_sessions (user_id, song_id, started_at, listened_ms, last_position_ms)
|
|---|
| 451 | VALUES (p_user_id, p_song_id, now(), 0, 0)
|
|---|
| 452 | RETURNING id;
|
|---|
| 453 | $$;
|
|---|
| 454 |
|
|---|
| 455 | -- функција за update на сесијата согласно испратениот heartbeat од backend-от.
|
|---|
| 456 | -- доколку listened_ms надмине 30_000 поставениот тригер ќе направи запис во song_streams
|
|---|
| 457 |
|
|---|
| 458 | CREATE OR REPLACE FUNCTION update_playback_progress(
|
|---|
| 459 | p_session_id bigint,
|
|---|
| 460 | p_listened_ms integer,
|
|---|
| 461 | p_last_position_ms integer
|
|---|
| 462 | )
|
|---|
| 463 | RETURNS void
|
|---|
| 464 | LANGUAGE sql
|
|---|
| 465 | AS $$
|
|---|
| 466 | UPDATE playback_sessions
|
|---|
| 467 | SET listened_ms = p_listened_ms,
|
|---|
| 468 | last_position_ms = p_last_position_ms
|
|---|
| 469 | WHERE id = p_session_id;
|
|---|
| 470 | $$;
|
|---|