diff --git a/src/database.erl b/src/database.erl index b7bbbe1..a82bd27 100644 --- a/src/database.erl +++ b/src/database.erl @@ -69,21 +69,36 @@ init_db(Database) -> {division, text}, {other, text}], [{primary_key, [field]}, - {foreign_key, {[division], divisions, [division], "ON DELETE SET NULL"}}, + % Clear divisions that are deleted + {foreign_key, {[division], + divisions, + [division], + "ON DELETE SET NULL + ON UPDATE CASCADE"}}, {check, "other IN ('testing', 'skills')"}]), ok = sqlite3:create_table(Database, teams, [{team, text, [not_null]}, {division, text}, {inspection, blob}], [{primary_key, [team]}, - {foreign_key, {[division], divisions, [division], "ON DELETE SET NULL"}}]), + % Clear divisions that are deleted + {foreign_key, {[division], + divisions, + [division], + "ON DELETE SET NULL + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, matches, [{division, text, [not_null]}, {type, text, [not_null]}, {number, integer, [not_null]}], [{primary_key, [division, type, number]}, {check, "type IN ('practice', 'qualification')"}, - {foreign_key, {[division], divisions, [division], "ON DELETE CASCADE"}}]), + % Delete matches when division deleted + {foreign_key, {[division], + divisions, + [division], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, match_states, [{division, text, [not_null]}, {type, text, [not_null]}, @@ -91,7 +106,12 @@ init_db(Database) -> {tick, integer, [not_null]}, {state, text, [not_null]}], [{primary_key, [division, type, number, tick]}, - {foreign_key, {[division, type, number], matches, [division, type, number], "ON DELETE CASCADE"}}]), + % Delete states when match deleted + {foreign_key, {[division, type, number], + matches, + [division, type, number], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, match_teams, [{division, text, [not_null]}, {type, text, [not_null]}, @@ -103,8 +123,18 @@ init_db(Database) -> {check, "side IN ('red', 'blue')"}, {check, "anum >= 1 AND anum <= 2"}, {check, "type IN ('practice', 'qualification')"}, - {foreign_key, {[division, type, number], matches, [division, type, number], "ON DELETE CASCADE"}}, - {foreign_key, {[team], teams, [team], ""}}]), + % Delete team associations when match deleted + {foreign_key, {[division, type, number], + matches, + [division, type, number], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}, + % Restrict team from being deleted that's associated with a match + {foreign_key, {[team], + teams, + [team], + "ON DELETE RESTRICT + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, match_scores, [{division, text, [not_null]}, {type, text, [not_null]}, @@ -112,54 +142,103 @@ init_db(Database) -> {history, integer, [not_null]}, {uuid, integer, [not_null]}], [{primary_key, [division, type, number, history]}, - {foreign_key, {[uuid], scores, [uuid], "ON DELETE CASCADE"}}, + % Restrict scores from being deleted that are referenced + {foreign_key, {[uuid], + scores, + [uuid], + "ON DELETE RESTRICT + ON UPDATE CASCADE"}}, + % Restrict matches from being deleted that are scored {foreign_key, {[division, type, number], matches, [division, type, number], - "ON DELETE CASCADE"}}]), + "ON DELETE RESTRICT + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, brackets, [{division, text}, + {stage, integer, [not_null]}, {number, integer, [not_null]}], - [{primary_key, [division, number]}, - {foreign_key, {[division], divisions, [division], "ON DELETE CASCADE"}}]), + [{primary_key, [division, stage, number]}, + % Delete brackets when division deleted + {foreign_key, {[division], + divisions, + [division], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, bracket_states, [{division, text}, + {stage, integer, [not_null]}, {number, integer, [not_null]}, {tick, integer, [not_null]}, {state, text, [not_null]}], - [{primary_key, [division, number, tick]}, - {foreign_key, {[division, number], brackets, [division, number], "ON DELETE CASCADE"}}]), + [{primary_key, [division, stage, number, tick]}, + % Delete states when bracket deleted + {foreign_key, {[division, stage, number], + brackets, + [division, stage, number], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, elimination_alliances, [{division, text, [not_null]}, + {stage, integer, [not_null]}, {number, integer, [not_null]}, {side, text, [not_null]}, {anum, integer, [not_null]}], - [{primary_key, [division, number, side]}, + [{primary_key, [division, stage, number, side]}, {check, "side IN ('red', 'blue')"}, - {foreign_key, {[division, number], brackets, [division, number], "ON DELETE CASCADE"}}, - {foreign_key, {[division, anum], alliances, [division, number], ""}}]), + % Delete alliance associations when bracket deleted + {foreign_key, {[division, stage, number], + brackets, + [division, stage, number], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}, + % Restrict alliance from being deleted that's associated with a bracket + {foreign_key, {[division, anum], + alliances, + [division, number], + "ON DELETE RESTRICT + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, finals_alliances, [{division, text}, + {stage, integer, [not_null]}, {number, integer, [not_null]}, {side, text, [not_null]}, {adiv, text, [not_null]}], - [{primary_key, [division, number, side]}, + [{primary_key, [division, stage, number, side]}, {check, "side IN ('red', 'blue')"}, {check, "division IS NULL"}, - {foreign_key, {[division, number], brackets, [division, number], "ON DELETE CASCADE"}}, - {foreign_key, {[adiv], winners, [division], ""}}]), + % Delete alliance associations when bracket deleted + {foreign_key, {[division, stage, number], + brackets, + [division, stage, number], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}, + % Restrict winners from being deleted that's associated with a bracket + {foreign_key, {[adiv], + winners, + [division], + "ON DELETE RESTRICT + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, bracket_scores, [{division, text}, + {stage, integer, [not_null]}, {number, integer, [not_null]}, {instance, integer, [not_null]}, {history, integer, [not_null]}, {uuid, integer, [not_null]}], [{primary_key, [division, number, instance, history]}, - {foreign_key, {[uuid], scores, [uuid], "ON DELETE CASCADE"}}, - {foreign_key, {[division, number], + % Restrict scores from being deleted that are referenced + {foreign_key, {[uuid], + scores, + [uuid], + "ON DELETE RESTRICT + ON UPDATE CASCADE"}}, + % Restrict brackets from being deleted that are scored + {foreign_key, {[division, stage, number], brackets, - [division, number], - "ON DELETE CASCADE"}}]), + [division, stage, number], + "ON DELETE RESTRICT + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, scores, [{uuid, integer, [not_null]}, {autonomous, integer, [not_null]}, @@ -188,20 +267,40 @@ init_db(Database) -> ok = sqlite3:create_table(Database, alliances, [{division, text, [not_null]}, {number, integer, [not_null]}], [{primary_key, [division, number]}, - {foreign_key, {[division], divisions, [division], "ON DELETE CASCADE"}}]), + % Delete alliances when deleting division + {foreign_key, {[division], + divisions, + [division], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, alliance_members, [{division, text, [not_null]}, {number, integer, [not_null]}, {anum, integer, [not_null]}, {team, text, [not_null]}], [{primary_key, [division, number, anum]}, - {foreign_key, {[division, number], alliances, [division, number], "ON DELETE CASCADE"}}, - {foreign_key, {[team], teams, [team], "ON DELETE CASCADE"}}]), + % Delete alliance members when deleting alliances + {foreign_key, {[division, number], + alliances, + [division, number], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}, + % Restrict teams being deleted that are in alliances + {foreign_key, {[team], + teams, + [team], + "ON DELETE RESTRICT + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, winners, [{division, text, [not_null]}, {alliance, integer, [not_null]}], [{primary_key, [division]}, - {foreign_key, {[division, alliance], alliances, [division, number], "ON DELETE CASCADE"}}]), + % Restrict divisions from being deleted that have winners + {foreign_key, {[division, alliance], + alliances, + [division, number], + "ON DELETE CASCADE + ON UPDATE CASCADE"}}]), ok = sqlite3:create_table(Database, skills_scores, [{team, text, [not_null]}, {type, text, [not_null]}, @@ -213,7 +312,12 @@ init_db(Database) -> {azone, integer, [not_null]}], [{primary_key, [team, type, attempt]}, {check, "type IN ('driver', 'autonomous')"}, - {foreign_key, {[team], teams, [team], ""}}]), + % Prevent teams being deleted that have skills scores + {foreign_key, {[team], + teams, + [team], + "ON DELETE RESTRICT + ON UPDATE CASCADE"}}]), ok. delete_teams(Database, Teams) -> @@ -405,11 +509,7 @@ handle_call({generate_elimination, Division, Size}, _, State) -> io_lib:format("DELETE FROM brackets WHERE division = '~s'; ", [Division]), lists:append([io_lib:format("INSERT INTO alliances(division, number) VALUES('~s', ~p); ", [Division, N]) - || N <- lists:seq(1, Size)]), - lists:append([io_lib:format("INSERT INTO brackets(division, number) - VALUES('~s', ~p);", - [Division, N]) - || N <- lists:seq(1, Size-1)])]), + || N <- lists:seq(1, Size)])]), {reply, sql_begin(State#state.database, SQL), State}; handle_call(generate_finals, _, State) ->